대략 다음과 같은 것이 있습니다. 기본적으로 정의에서 인스턴스 메서드에 사용되는 데코레이터에서 인스턴스 메서드의 클래스에 액세스해야합니다.
def decorator(view):
# do something that requires view's class
print view.im_class
return view
class ModelA(object):
@decorator
def a_method(self):
# do some stuff
pass
있는 그대로 코드는 다음을 제공합니다.
AttributeError : ‘function’개체에 ‘im_class’속성이 없습니다.
– 나는 비슷한 질문 / 답변을 발견 파이썬 장식의 차종이 클래스에 속한다는 것을 잊지 기능 및 파이썬 장식의 클래스를 돌려 – 그러나 이들은 첫 번째 매개 변수를 날치기로 런타임에 인스턴스를 잡고 해결 방법에 의존한다. 제 경우에는 클래스에서 수집 한 정보를 기반으로 메서드를 호출 할 것이므로 호출이 올 때까지 기다릴 수 없습니다.
답변
Python 2.6 이상을 사용하는 경우 다음과 같은 클래스 데코레이터를 사용할 수 있습니다 (경고 : 테스트되지 않은 코드).
def class_decorator(cls):
for name, method in cls.__dict__.iteritems():
if hasattr(method, "use_class"):
# do something with the method and class
print name, cls
return cls
def method_decorator(view):
# mark the method as something that requires view's class
view.use_class = True
return view
@class_decorator
class ModelA(object):
@method_decorator
def a_method(self):
# do some stuff
pass
메소드 데코레이터는 “use_class”속성을 추가하여 메소드를 관심있는 것으로 표시합니다. 함수와 메소드도 객체이므로 추가 메타 데이터를 첨부 할 수 있습니다.
클래스가 생성 된 후 클래스 데코레이터는 모든 메서드를 살펴보고 표시된 메서드에 필요한 모든 작업을 수행합니다.
모든 메소드가 영향을 받기를 원한다면 메소드 데코레이터를 생략하고 클래스 데코레이터를 사용할 수 있습니다.
답변
파이썬 3.6부터는 object.__set_name__
매우 간단한 방법으로이를 수행 할 수 있습니다 . 문서에는 __set_name__
“소유 클래스 소유자 가 생성 될 때 호출됩니다”라고 명시되어 있습니다. 다음은 예입니다.
class class_decorator:
def __init__(self, fn):
self.fn = fn
def __set_name__(self, owner, name):
# do something with owner, i.e.
print(f"decorating {self.fn} and using {owner}")
self.fn.class_name = owner.__name__
# then replace ourself with the original method
setattr(owner, name, self.fn)
클래스 생성시 호출됩니다.
>>> class A:
... @class_decorator
... def hello(self, x=42):
... return x
...
decorating <function A.hello at 0x7f9bedf66bf8> and using <class '__main__.A'>
>>> A.hello
<function __main__.A.hello(self, x=42)>
>>> A.hello.class_name
'A'
>>> a = A()
>>> a.hello()
42
클래스가 어떻게 생성되는지, 특히 정확히 언제 __set_name__
호출 되는지에 대해 더 알고 싶다면 “Creating the class object”문서를 참조하십시오 .
답변
다른 사람들이 지적했듯이 데코레이터가 호출 될 때 클래스가 생성되지 않았습니다. 그러나 데코레이터 매개 변수로 함수 객체에 주석을 단 다음 메타 클래스의 __new__
메서드 에서 함수를 다시 데코레이션 할 수 있습니다 . __dict__
적어도 저 func.foo = 1
에게는 AttributeError가 발생했듯이 함수의 속성에 직접 액세스해야합니다 .
답변
Mark가 제안한대로 :
- 모든 데코레이터는 클래스가 빌드되기 전에 호출되므로 데코레이터는 알 수 없습니다.
- 이러한 메서드에 태그를 지정 하고 나중에 필요한 사후 처리를 수행 할 수 있습니다 .
- 사후 처리를위한 두 가지 옵션이 있습니다. 클래스 정의가 끝날 때 자동으로 또는 애플리케이션이 실행되기 전 어딘가에 있습니다. 기본 클래스를 사용하는 첫 번째 옵션을 선호하지만 두 번째 방법을 따를 수도 있습니다.
이 코드는 자동 후 처리를 사용하여 어떻게 작동하는지 보여줍니다.
def expose(**kw):
"Note that using **kw you can tag the function with any parameters"
def wrap(func):
name = func.func_name
assert not name.startswith('_'), "Only public methods can be exposed"
meta = func.__meta__ = kw
meta['exposed'] = True
return func
return wrap
class Exposable(object):
"Base class to expose instance methods"
_exposable_ = None # Not necessary, just for pylint
class __metaclass__(type):
def __new__(cls, name, bases, state):
methods = state['_exposed_'] = dict()
# inherit bases exposed methods
for base in bases:
methods.update(getattr(base, '_exposed_', {}))
for name, member in state.items():
meta = getattr(member, '__meta__', None)
if meta is not None:
print "Found", name, meta
methods[name] = member
return type.__new__(cls, name, bases, state)
class Foo(Exposable):
@expose(any='parameter will go', inside='__meta__ func attribute')
def foo(self):
pass
class Bar(Exposable):
@expose(hide=True, help='the great bar function')
def bar(self):
pass
class Buzz(Bar):
@expose(hello=False, msg='overriding bar function')
def bar(self):
pass
class Fizz(Foo):
@expose(msg='adding a bar function')
def bar(self):
pass
print('-' * 20)
print("showing exposed methods")
print("Foo: %s" % Foo._exposed_)
print("Bar: %s" % Bar._exposed_)
print("Buzz: %s" % Buzz._exposed_)
print("Fizz: %s" % Fizz._exposed_)
print('-' * 20)
print('examine bar functions')
print("Bar.bar: %s" % Bar.bar.__meta__)
print("Buzz.bar: %s" % Buzz.bar.__meta__)
print("Fizz.bar: %s" % Fizz.bar.__meta__)
결과는 다음과 같습니다.
Found foo {'inside': '__meta__ func attribute', 'any': 'parameter will go', 'exposed': True}
Found bar {'hide': True, 'help': 'the great bar function', 'exposed': True}
Found bar {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
Found bar {'msg': 'adding a bar function', 'exposed': True}
--------------------
showing exposed methods
Foo: {'foo': <function foo at 0x7f7da3abb398>}
Bar: {'bar': <function bar at 0x7f7da3abb140>}
Buzz: {'bar': <function bar at 0x7f7da3abb0c8>}
Fizz: {'foo': <function foo at 0x7f7da3abb398>, 'bar': <function bar at 0x7f7da3abb488>}
--------------------
examine bar functions
Bar.bar: {'hide': True, 'help': 'the great bar function', 'exposed': True}
Buzz.bar: {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
Fizz.bar: {'msg': 'adding a bar function', 'exposed': True}
이 예에서는 다음을 참고하십시오.
- 임의의 매개 변수로 모든 함수에 주석을 달 수 있습니다.
- 각 클래스에는 자체 노출 된 메서드가 있습니다.
- 노출 된 메서드도 상속 할 수 있습니다.
- 노출 기능이 업데이트되면 메서드를 재정의 할 수 있습니다.
도움이 되었기를 바랍니다
답변
Ants가 지적했듯이 클래스 내에서 클래스에 대한 참조를 가져올 수 없습니다. 그러나 실제 클래스 유형 객체를 조작하지 않고 다른 클래스를 구별하는 데 관심이있는 경우 각 클래스에 대한 문자열을 전달할 수 있습니다. 클래스 스타일 데코레이터를 사용하여 원하는 다른 매개 변수를 데코레이터에 전달할 수도 있습니다.
class Decorator(object):
def __init__(self,decoratee_enclosing_class):
self.decoratee_enclosing_class = decoratee_enclosing_class
def __call__(self,original_func):
def new_function(*args,**kwargs):
print 'decorating function in ',self.decoratee_enclosing_class
original_func(*args,**kwargs)
return new_function
class Bar(object):
@Decorator('Bar')
def foo(self):
print 'in foo'
class Baz(object):
@Decorator('Baz')
def foo(self):
print 'in foo'
print 'before instantiating Bar()'
b = Bar()
print 'calling b.foo()'
b.foo()
인쇄물:
before instantiating Bar()
calling b.foo()
decorating function in Bar
in foo
답변
무엇 플라스크 – 고급이 는 방식에 저장하는 임시 캐시를 만드는 것입니다 않습니다, 그것은 뭔가 다른 (플라스크를 사용하여 클래스를 등록 것이라는 사실 사용 register
에 실제로 방법을 래핑 클래스의 방법).
이 패턴을 재사용 할 수 있습니다. 이번에는 메타 클래스를 사용하여 가져올 때 메서드를 래핑 할 수 있습니다.
def route(rule, **options):
"""A decorator that is used to define custom routes for methods in
FlaskView subclasses. The format is exactly the same as Flask's
`@app.route` decorator.
"""
def decorator(f):
# Put the rule cache on the method itself instead of globally
if not hasattr(f, '_rule_cache') or f._rule_cache is None:
f._rule_cache = {f.__name__: [(rule, options)]}
elif not f.__name__ in f._rule_cache:
f._rule_cache[f.__name__] = [(rule, options)]
else:
f._rule_cache[f.__name__].append((rule, options))
return f
return decorator
실제 클래스에서 (메타 클래스를 사용하여 동일한 작업을 수행 할 수 있음) :
@classmethod
def register(cls, app, route_base=None, subdomain=None, route_prefix=None,
trailing_slash=None):
for name, value in members:
proxy = cls.make_proxy_method(name)
route_name = cls.build_route_name(name)
try:
if hasattr(value, "_rule_cache") and name in value._rule_cache:
for idx, cached_rule in enumerate(value._rule_cache[name]):
# wrap the method here
출처 : https://github.com/apiguy/flask-classy/blob/master/flask_classy.py
답변
문제는 데코레이터가 호출 될 때 클래스가 아직 존재하지 않는다는 것입니다. 이 시도:
def loud_decorator(func):
print("Now decorating %s" % func)
def decorated(*args, **kwargs):
print("Now calling %s with %s,%s" % (func, args, kwargs))
return func(*args, **kwargs)
return decorated
class Foo(object):
class __metaclass__(type):
def __new__(cls, name, bases, dict_):
print("Creating class %s%s with attributes %s" % (name, bases, dict_))
return type.__new__(cls, name, bases, dict_)
@loud_decorator
def hello(self, msg):
print("Hello %s" % msg)
Foo().hello()
이 프로그램은 다음을 출력합니다.
Now decorating <function hello at 0xb74d35dc>
Creating class Foo(<type 'object'>,) with attributes {'__module__': '__main__', '__metaclass__': <class '__main__.__metaclass__'>, 'hello': <function decorated at 0xb74d356c>}
Now calling <function hello at 0xb74d35dc> with (<__main__.Foo object at 0xb74ea1ac>, 'World'),{}
Hello World
보시다시피, 원하는 작업을 수행하는 다른 방법을 찾아야합니다.