자기 주장이 있는 클래스 메서드 데코레이터?
클래스 메서드에서 클래스 필드를 어떻게 인수로 장식자에게 전달합니까?제가 하고 싶은 일은 다음과 같습니다.
class Client(object):
def __init__(self, url):
self.url = url
@check_authorization("some_attr", self.url)
def get(self):
do_work()
합격하기 위해 자아가 존재하지 않는다고 불평한다.self.url
데코레이터에게.이 문제를 해결할 방법이 있나요?
예. 클래스 정의 시 인스턴스 속성을 전달하는 대신 런타임에 확인합니다.
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
@check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
데코레이터는 메서드 인수를 대행 수신합니다.첫 번째 인수는 인스턴스이므로 여기서 속성을 읽습니다.속성 이름을 문자열로 데코레이터에 전달하고 다음을 사용할 수 있습니다.getattr
Atribute name을 하드코드하지 않는 경우:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
보다 간결한 예는 다음과 같습니다.
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
@wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
@wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
인쇄 대상:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
@wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
@is_match(lambda x: x.name, 'foo')
@is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
@is_match(lambda x: x.name, 'foo')
@is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
출력: my_rule2: OK
또 다른 선택사항은 통사당(통사당)을 버리고 그 안에 장식하는 것이다.__init__
클래스입니다.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
인쇄하는 방법
3
2
1
im doing stuff!
이 문제가 상당히 오래된 문제인 것은 알지만, 아래의 회피책은 이전에 제안되지 않았습니다.여기서 문제가 되는 것은 당신이 접속할 수 없다는 것이다.self
클래스 블록에서는 사용할 수 있지만 클래스 메서드에서는 사용할 수 있습니다.
기능을 몇 번 반복할 수 있는 더미 데코레이터를 만들어 봅시다.
import functools
def repeat(num_rep):
def decorator_repeat(func):
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
@repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
오래된 질문인 것은 알지만, 이 해결책은 아직 언급되지 않았습니다. 8년이 지난 오늘날에도 도움이 될 수 있기를 바랍니다.
그럼, 포장지를 싸는 건 어때요?예를 들어 init에서는 데코레이터를 변경할 수 없고, 그 메서드를 꾸밀 수도 없다고 가정합니다(@property decored 등).자신을 캡처한 후 원래 데코레이터를 호출하여 런타임 속성을 전달하는 커스텀 클래스 고유의 데코레이터를 생성할 수 있습니다.
다음으로 동작 예를 나타냅니다(f스트링에는 python 3.6이 필요합니다).
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
@custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
출력:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
그럴수는 없어요.없습니다.self
인스턴스가 존재하지 않기 때문에 클래스 본문에 포함되어 있습니다.넘겨야 할 것 같은데, 예를 들면...str
인스턴스에서 조회할 속성 이름이 포함되어 있으며 반환된 함수가 이를 수행하거나 완전히 다른 메서드를 사용할 수 있습니다.
기능용 데코레이터를 메서드 데코레이터로 바꿀 수 있는 범용 유틸리티가 있으면 매우 편리합니다.한 시간 동안 고민하다가 생각해낸 게 있어요
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
@dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
사용법은 다음과 같습니다.
# for any decorator (with or without arguments)
@some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
@decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
테스트:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
@dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
@dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
@decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)
언급URL : https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments
'programing' 카테고리의 다른 글
같은 테이블의 다른 행의 데이터로 행 업데이트 (0) | 2022.12.20 |
---|---|
리소스 텍스트 파일을 문자열(Java)로 읽기 위한 유틸리티 (0) | 2022.12.20 |
CSV 파일을 MySQL 테이블로 Import하려면 어떻게 해야 합니까? (0) | 2022.12.20 |
MySQL: 데이터베이스 삭제 오류(errno 13, errno 17, errno 39) (0) | 2022.12.20 |
Larabel Mix에 Font Awesome을 설치하는 방법 (0) | 2022.12.20 |