Python 2.5에서 클래스를 장식하는 데코레이터를 만드는 방법이 있습니까? 특히 데코레이터를 사용하여 클래스에 멤버를 추가하고 해당 멤버의 값을 갖도록 생성자를 변경하고 싶습니다.
다음과 같은 것을 찾으십시오 ( ‘class Foo :’에 구문 오류가 있습니다.)
def getId(self): return self.__id
class addID(original_class):
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
original_class.__init__(self, *args, **kws)
@addID
class Foo:
def __init__(self, value1):
self.value1 = value1
if __name__ == '__main__':
foo1 = Foo(5,1)
print foo1.value1, foo1.getId()
foo2 = Foo(15,2)
print foo2.value1, foo2.getId()
필자는 실제로 파이썬에서 C # 인터페이스와 같은 것을 수행하는 방법이라고 생각합니다. 내가 생각하는 패러다임을 바꿔야합니다.
답변
나는 당신이 설명한 접근법 대신 서브 클래스를 고려할 수 있다는 개념을 두 번째로 생각합니다. 그러나 특정 시나리오를 모르면 YMMV 🙂
당신이 생각하는 것은 메타 클래스입니다. __new__
메타 클래스 의 함수는 클래스의 전체 제안 된 정의를 전달한 다음 클래스를 작성하기 전에 다시 작성할 수 있습니다. 이때 새 생성자를 생성 할 수 있습니다.
예:
def substitute_init(self, id, *args, **kwargs):
pass
class FooMeta(type):
def __new__(cls, name, bases, attrs):
attrs['__init__'] = substitute_init
return super(FooMeta, cls).__new__(cls, name, bases, attrs)
class Foo(object):
__metaclass__ = FooMeta
def __init__(self, value1):
pass
생성자를 교체하는 것은 다소 극적인 일이지만 언어는 이러한 종류의 깊은 내성 및 동적 수정을 지원합니다.
답변
클래스 데코레이터가 귀하의 문제에 적합한 솔루션인지 여부에 대한 질문 외에도 :
Python 2.6 이상에는 @ -syntax가 포함 된 클래스 데코레이터가 있으므로 다음과 같이 작성할 수 있습니다.
@addID
class Foo:
pass
이전 버전에서는 다른 방법으로 수행 할 수 있습니다.
class Foo:
pass
Foo = addID(Foo)
그러나 이것은 함수 데코레이터와 동일하게 작동하며 데코레이터는 예제에서 수행하지 않은 새 클래스 또는 수정 된 원래 클래스를 반환해야합니다. addID 데코레이터는 다음과 같습니다.
def addID(original_class):
orig_init = original_class.__init__
# Make copy of original __init__, so we can call it without recursion
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
orig_init(self, *args, **kws) # Call the original __init__
original_class.__init__ = __init__ # Set the class' __init__ to the new one
return original_class
그런 다음 위에서 설명한대로 Python 버전에 적절한 구문을 사용할 수 있습니다.
그러나 나는 당신이 재정의하기를 원한다면 상속이 더 적합하다고 다른 사람들에게 동의한다 __init__
.
답변
아무도 클래스를 동적으로 정의 할 수 있다고 설명하지 않았습니다. 따라서 서브 클래스를 정의하고 리턴하는 데코레이터를 가질 수 있습니다.
def addId(cls):
class AddId(cls):
def __init__(self, id, *args, **kargs):
super(AddId, self).__init__(*args, **kargs)
self.__id = id
def getId(self):
return self.__id
return AddId
다음과 같이 Python 2 (Blckknght의 의견으로 2.6 이상에서 계속 해야하는 이유)에서 사용할 수 있습니다.
class Foo:
pass
FooId = addId(Foo)
그리고 파이썬 3에서는 이와 같이 (그러나 super()
클래스에서 조심스럽게 사용 하십시오) :
@addId
class Foo:
pass
그래서 당신은 당신의 케이크를 가질 수 와 먹을 – 상속 과 데코레이터!
답변
그것은 좋은 습관이 아니며 그 때문에 그렇게 할 수있는 메커니즘이 없습니다. 원하는 것을 달성하는 올바른 방법은 상속입니다.
수업 문서를 살펴보십시오 .
작은 예 :
class Employee(object):
def __init__(self, age, sex, siblings=0):
self.age = age
self.sex = sex
self.siblings = siblings
def born_on(self):
today = datetime.date.today()
return today - datetime.timedelta(days=self.age*365)
class Boss(Employee):
def __init__(self, age, sex, siblings=0, bonus=0):
self.bonus = bonus
Employee.__init__(self, age, sex, siblings)
이런 식으로 보스는 Employee
자신의 __init__
방법과 회원과 함께 모든 것을 가지고 있습니다.
답변
상속이 문제에 더 적합하다는 데 동의합니다.
수업을 꾸미는 데이 질문이 정말 편리하다는 것을 알았습니다. 감사합니다.
상속이 파이썬 2.7 (그리고 원래 함수의 docstring 등을 유지하는 @wraps 등) 에서 어떻게 영향을 받는지를 포함하여 다른 답변을 기반으로 한 또 다른 예가 있습니다 .
def dec(klass):
old_foo = klass.foo
@wraps(klass.foo)
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
@dec # No parentheses
class Foo...
종종 데코레이터에 매개 변수를 추가하려고합니다.
from functools import wraps
def dec(msg='default'):
def decorator(klass):
old_foo = klass.foo
@wraps(klass.foo)
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
return decorator
@dec('foo decorator') # You must add parentheses now, even if they're empty
class Foo(object):
def foo(self, *args, **kwargs):
print('foo.foo()')
@dec('subfoo decorator')
class SubFoo(Foo):
def foo(self, *args, **kwargs):
print('subfoo.foo() pre')
super(SubFoo, self).foo(*args, **kwargs)
print('subfoo.foo() post')
@dec('subsubfoo decorator')
class SubSubFoo(SubFoo):
def foo(self, *args, **kwargs):
print('subsubfoo.foo() pre')
super(SubSubFoo, self).foo(*args, **kwargs)
print('subsubfoo.foo() post')
SubSubFoo().foo()
출력 :
@decorator pre subsubfoo decorator
subsubfoo.foo() pre
@decorator pre subfoo decorator
subfoo.foo() pre
@decorator pre foo decorator
foo.foo()
@decorator post foo decorator
subfoo.foo() post
@decorator post subfoo decorator
subsubfoo.foo() post
@decorator post subsubfoo decorator
나는 그들이 더 간결하다는 것을 알기 때문에 함수 데코레이터를 사용했습니다. 클래스를 장식하는 클래스는 다음과 같습니다.
class Dec(object):
def __init__(self, msg):
self.msg = msg
def __call__(self, klass):
old_foo = klass.foo
msg = self.msg
def decorated_foo(self, *args, **kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
괄호를 확인하고 장식 된 클래스에 메소드가없는 경우 작동하는보다 강력한 버전입니다.
from inspect import isclass
def decorate_if(condition, decorator):
return decorator if condition else lambda x: x
def dec(msg):
# Only use if your decorator's first parameter is never a class
assert not isclass(msg)
def decorator(klass):
old_foo = getattr(klass, 'foo', None)
@decorate_if(old_foo, wraps(klass.foo))
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
if callable(old_foo):
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
return decorator
assert
검사는 장식 괄호없이 사용되지 않았 음. 있는 경우 데코 레이팅되는 클래스 msg
는 데코레이터 의 매개 변수로 전달 되어을 발생 AssertionError
시킵니다.
@decorate_if
decorator
if condition
평가 에만 적용됩니다 True
.
getattr
, callable
테스트 및 @decorate_if
경우 장식이 중단되지 않도록 사용되는 foo()
방법은 클래스가 장식 되에 존재하지 않습니다.
답변
실제로 여기에는 클래스 데코레이터의 구현이 꽤 있습니다.
https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py
실제로 이것은 꽤 흥미로운 구현이라고 생각합니다. 그것이 장식하는 클래스를 서브 클래 싱하기 때문에, isinstance
체크 와 같은 것들에서이 클래스와 똑같이 동작 합니다.
추가 장점이 있습니다. __init__
사용자 정의 장고 양식 의 문이 수정되거나 추가되는 것은 드문 일 이 self.fields
아니므 로 문제의 클래스에 대해 모든 실행 self.fields
이 완료된 후에 변경 이 발생하는 것이 좋습니다 __init__
.
매우 영리한.
그러나 클래스에서는 실제로 장식이 생성자를 변경하여 클래스 데코레이터의 좋은 사용 사례라고 생각하지 않습니다.
답변
다음은 클래스의 매개 변수를 반환하는 문제에 대한 답변입니다. 또한 여전히 상속 체인을 존중합니다. 즉 클래스 자체의 매개 변수 만 반환됩니다. 이 기능 get_params
은 간단한 예로 추가되었지만 inspect 모듈 덕분에 다른 기능도 추가 할 수 있습니다.
import inspect
class Parent:
@classmethod
def get_params(my_class):
return list(inspect.signature(my_class).parameters.keys())
class OtherParent:
def __init__(self, a, b, c):
pass
class Child(Parent, OtherParent):
def __init__(self, x, y, z):
pass
print(Child.get_params())
>>['x', 'y', 'z']