Python에서 읽기 전용 클래스 속성을 만드는 방법은 무엇입니까?
기본적으로 다음과 같이하고 싶습니다.
class foo:
x = 4
@property
@classmethod
def number(cls):
return x
그런 다음 다음 작업을 원합니다.
>>> foo.number
4
불행히도 위의 내용은 작동하지 않습니다. 나 4
에게주는 대신에 나 에게 준다 <property object at 0x101786c58>
. 위의 사항을 달성하는 방법이 있습니까?
property
클래스에서 액세스 할 때 (즉.시 기술자는 항상 자신을 반환하는 instance
것입니다 None
그것의 __get__
방법).
원하는 것이 아니라면 항상 owner
인스턴스 대신 클래스 객체 ( )를 사용하는 새 설명자를 작성할 수 있습니다 .
>>> class classproperty(object):
... def __init__(self, getter):
... self.getter= getter
... def __get__(self, instance, owner):
... return self.getter(owner)
...
>>> class Foo(object):
... x= 4
... @classproperty
... def number(cls):
... return cls.x
...
>>> Foo().number
4
>>> Foo.number
4
이 것 읽기 전용 속성을 :Foo.number
class MetaFoo(type):
@property
def number(cls):
return cls.x
class Foo(object, metaclass=MetaFoo):
x = 4
print(Foo.number)
# 4
Foo.number = 6
# AttributeError: can't set attribute
설명 : @property를 사용할 때의 일반적인 시나리오는 다음과 같습니다.
class Foo(object):
@property
def number(self):
...
foo = Foo()
에 정의 된 속성 Foo
은 해당 인스턴스와 관련하여 읽기 전용입니다. 즉, foo.number = 6
을 올릴 것입니다 AttributeError
.
마찬가지로,를 발생 Foo.number
시키 려면 에 AttributeError
정의 된 속성을 설정해야합니다 type(Foo)
. 따라서 메타 클래스가 필요합니다.
이 읽기 전용은 해커로부터 영향을받지 않습니다. 속성은 Foo의 클래스를 변경하여 쓰기 가능하게 만들 수 있습니다.
class Base(type): pass
Foo.__class__ = Base
# makes Foo.number a normal class attribute
Foo.number = 6
print(Foo.number)
인쇄물
6
또는 Foo.number
설정 가능한 속성 을 만들려면
class WritableMetaFoo(type):
@property
def number(cls):
return cls.x
@number.setter
def number(cls, value):
cls.x = value
Foo.__class__ = WritableMetaFoo
# Now the assignment modifies `Foo.x`
Foo.number = 6
print(Foo.number)
또한 6을 인쇄합니다.
나는 unubtu의 대답에 동의합니다 . 그러나 작동하는 것처럼 보이지만 Python 3 에서이 정확한 구문으로 작동하지 않습니다 (특히 Python 3.4는 내가 어려움을 겪은 것입니다). 다음은 일을 작동시키기 위해 Python 3.4에서 패턴을 형성해야하는 방법입니다.
class MetaFoo(type):
@property
def number(cls):
return cls.x
class Foo(metaclass=MetaFoo):
x = 4
print(Foo.number)
# 4
Foo.number = 6
# AttributeError: can't set attribute
Mikhail Gerasimov의 솔루션은 매우 완벽합니다. 불행히도 그것은 한 가지 단점이었습니다. 그의 classproperty를 사용하는 클래스가 있으면 TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
with 로 인해 자식 클래스가 사용할 수 없습니다 class Wrapper
.
다행히도이 문제를 해결할 수 있습니다. .NET Framework를 만들 때 지정된 클래스의 메타 클래스에서 상속하기 만하면됩니다 class Meta
.
def classproperty_support(cls):
"""
Class decorator to add metaclass to our class.
Metaclass uses to add descriptors to class attributes, see:
http://stackoverflow.com/a/26634248/1113207
"""
# Use type(cls) to use metaclass of given class
class Meta(type(cls)):
pass
for name, obj in vars(cls).items():
if isinstance(obj, classproperty):
setattr(Meta, name, property(obj.fget, obj.fset, obj.fdel))
class Wrapper(cls, metaclass=Meta):
pass
return Wrapper
위의 솔루션의 문제는 인스턴스 변수에서 클래스 변수에 액세스하는 데 작동하지 않는다는 것입니다.
print(Foo.number)
# 4
f = Foo()
print(f.number)
# 'Foo' object has no attribute 'number'
게다가 명시 적 메타 클래스를 사용하는 것은 일반 property
데코레이터 를 사용하는 것만 큼 좋지 않습니다 .
이 문제를 해결하려고 노력했습니다. 지금 어떻게 작동하는지 :
@classproperty_support
class Bar(object):
_bar = 1
@classproperty
def bar(cls):
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
# @classproperty should act like regular class variable.
# Asserts can be tested with it.
# class Bar:
# bar = 1
assert Bar.bar == 1
Bar.bar = 2
assert Bar.bar == 2
foo = Bar()
baz = Bar()
assert foo.bar == 2
assert baz.bar == 2
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
보시다시피 클래스 변수 @classproperty
와 동일한 방식으로 작동 @property
합니다. 필요한 것은 추가 @classproperty_support
클래스 데코레이터 뿐입니다 .
솔루션은 읽기 전용 클래스 속성에서도 작동합니다.
구현은 다음과 같습니다.
class classproperty:
"""
Same as property(), but passes obj.__class__ instead of obj to fget/fset/fdel.
Original code for property emulation:
https://docs.python.org/3.5/howto/descriptor.html#properties
"""
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj.__class__)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj.__class__, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj.__class__)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
def classproperty_support(cls):
"""
Class decorator to add metaclass to our class.
Metaclass uses to add descriptors to class attributes, see:
http://stackoverflow.com/a/26634248/1113207
"""
class Meta(type):
pass
for name, obj in vars(cls).items():
if isinstance(obj, classproperty):
setattr(Meta, name, property(obj.fget, obj.fset, obj.fdel))
class Wrapper(cls, metaclass=Meta):
pass
return Wrapper
Note: code isn't tested much, feel free to note if it doesn't work as you expect.
참고URL : https://stackoverflow.com/questions/3203286/how-to-create-a-read-only-class-property-in-python
'Development Tip' 카테고리의 다른 글
2 배에서 x 개의 유효 숫자로 반올림 (0) | 2020.11.23 |
---|---|
배열 이름을 복수 또는 단수로 지정합니까? (0) | 2020.11.23 |
IntelliJ가 "새"표현식에서 생성자 매개 변수를 자동 완성 할 수 있습니까? (0) | 2020.11.23 |
codeigniter 활성 레코드의 하위 쿼리 (0) | 2020.11.23 |
기본 클래스 생성자를 어떻게 호출합니까? (0) | 2020.11.23 |