클래스 속성을 만드는 방법?
할 수 .@classmethod
할 수 있는 ?클래스에 속성을 추가할 수 있는 비슷한 장식가가 있습니까?내가 무슨 말을 하는지 더 잘 보여줄 수 있어요.
class Example(object):
the_I = 10
def __init__( self ):
self.an_i = 20
@property
def i( self ):
return self.an_i
def inc_i( self ):
self.an_i += 1
# is this even possible?
@classproperty
def I( cls ):
return cls.the_I
@classmethod
def inc_I( cls ):
cls.the_I += 1
e = Example()
assert e.i == 20
e.inc_i()
assert e.i == 21
assert Example.I == 10
Example.inc_I()
assert Example.I == 11
위에서 사용한 구문이 가능한가요, 아니면 더 필요한가요?
제가 클래스 속성을 원하는 이유는 클래스 속성을 느리게 로드할 수 있기 때문입니다. 충분히 합리적인 것 같습니다.
다음과 같은 방법이 있습니다.
class ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class Bar(object):
_bar = 1
@classproperty
def bar(cls):
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
# test instance instantiation
foo = Bar()
assert foo.bar == 1
baz = Bar()
assert baz.bar == 1
# test static variable
baz.bar = 5
assert foo.bar == 5
# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
우리가 전화를 걸 때 세터가 작동하지 않았습니다.Bar.bar
우리가 전화하고 있기 때문에TypeOfBar.bar.__set__
은 지렇않이 .Bar.bar.__set__
.
메타클래스 정의를 추가하면 다음과 같은 문제가 해결됩니다.
class ClassPropertyMetaClass(type):
def __setattr__(self, key, value):
if key in self.__dict__:
obj = self.__dict__.get(key)
if obj and type(obj) is ClassPropertyDescriptor:
return obj.__set__(self, value)
return super(ClassPropertyMetaClass, self).__setattr__(key, value)
# and update class define:
# class Bar(object):
# __metaclass__ = ClassPropertyMetaClass
# _bar = 1
# and update ClassPropertyDescriptor.__set__
# def __set__(self, obj, value):
# if not self.fset:
# raise AttributeError("can't set attribute")
# if inspect.isclass(obj):
# type_ = obj
# obj = None
# else:
# type_ = type(obj)
# return self.fset.__get__(obj, type_)(value)
이제 다 괜찮을 겁니다.
당신이 경우는정을 정의한다면.classproperty
다음과 같이, 당신의 예는 당신이 요청한 대로 정확히 작동합니다.
class classproperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
주의할 점은 쓰기 가능한 속성에는 이 기능을 사용할 수 없다는 것입니다.하는 동안에e.I = 20
을 올릴 것입니다.AttributeError
,Example.I = 20
속성 개체 자체를 덮어씁니다.
[python 3.4를 기반으로 작성된 답변, 메타클래스 구문은 2개가 다르지만 기술은 여전히 작동할 것으로 생각합니다.
메타 클래스로 할 수 있어요 대부분은...Dappawit은 거의 효과가 있지만 결함이 있다고 생각합니다.
class MetaFoo(type):
@property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
푸에 대한 클래스 속성을 얻을 수 있습니다. 하지만 문제가 있습니다.
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
이게 대체 무슨 일입니까?인스턴스에서 클래스 속성에 연결할 수 없는 이유는 무엇입니까?
저는 제가 믿는 답을 찾기 전에 꽤 오랫동안 이것에 대해 머리를 굴리고 있었습니다.Python @properties는 설명자의 하위 집합이며, 설명자 문서(마인 강조)에서 다음과 같이 설명합니다.
속성 액세스의 기본 동작은 개체의 사전에서 속성을 가져오거나 설정 또는 삭제하는 것입니다.를 들면 예를들어들.
a.x
에는 다으로시룩체있업습다로 이 있습니다.a.__dict__['x']
,그리고나서type(a).__dict__['x']
해서 그고기클계를다속니합래스본리의 을 진행합니다.type(a)
메타 클래스를 제외합니다.
따라서 메서드 확인 순서에는 클래스 속성(또는 메타 클래스에 정의된 다른 속성)이 포함되지 않습니다.다르게 동작하는 빌트인 속성 장식가의 하위 클래스를 만드는 것은 가능하지만, (인용이 필요합니다) 저는 개발자들이 그런 식으로 하는 것에 대해 (이해할 수 없는) 충분한 이유가 있다는 인상을 받았습니다.
그렇다고 해서 운이 다했다는 뜻은 아닙니다. 클래스 자체에 있는 숙박시설에 액세스할 수 있습니다.그리고 우리는 수업을 받을 수 있습니다.type(self)
인스턴스 내에서, 우리는 이를 @dispatchers를 만드는 데 사용할 수 있습니다.
class Foo(object, metaclass=MetaFoo):
_thingy = 23
@property
def thingy(self):
return type(self).thingy
지금이다Foo().thingy
클래스와 인스턴스 모두에 대해 의도한 대로 작동합니다!파생 클래스가 기본 클래스를 대체하는 경우에도 올바른 작업을 계속 수행합니다._thingy
(이것이 제가 원래 이 사냥에 참여하게 된 사용 사례입니다.)
메타 클래스와 객체 클래스 모두에서 설정을 수행해야 하는 것은 DRY 원칙을 위반하는 것처럼 느껴집니다.하지만 후자는 단지 한 줄짜리 디스패처일 뿐입니다. 저는 그것이 존재하는 것에 대해 대부분 만족합니다. 그리고 당신이 정말 원한다면 람다나 다른 것으로 압축할 수도 있습니다.
장고를 사용하면 내장되어 있습니다.@classproperty
장식가
from django.utils.decorators import classproperty
장고 4의 경우 다음을 사용합니다.
from django.utils.functional import classproperty
메타 수업으로 이것을 할 수 있을 것 같습니다.메타 클래스는 클래스에 대한 클래스와 같을 수 있기 때문입니다(말이 되는 경우).나는 당신이 할당할 수 있다는 것을 압니다.__call__()
합니다.MyClass()
나는 그것을 사용하는지 궁금합니다.property
메타 클래스의 데코레이터는 유사하게 작동합니다.
와, 효과가 있습니다.
class MetaClass(type):
def getfoo(self):
return self._foo
foo = property(getfoo)
@property
def bar(self):
return self._bar
class MyClass(object):
__metaclass__ = MetaClass
_foo = 'abc'
_bar = 'def'
print MyClass.foo
print MyClass.bar
참고: 이것은 Python 2.7에 있습니다.Python 3+는 다른 기법을 사용하여 메타 클래스를 선언합니다. 사용:class MyClass(metaclass=MetaClass):
합니다.__metaclass__
나머지는 똑같습니다.
제가 알기로는 새로운 메타클래스를 만들지 않고는 클래스 속성에 대한 세터를 작성할 수 없습니다.
저는 다음과 같은 방법이 효과가 있다는 것을 알았습니다.원하는 모든 클래스 속성 및 설정자를 사용하여 메타 클래스를 정의합니다.IE, 나는 수업을 듣고 싶었습니다.title
쓴 과 같습니다제가 쓴 글은 다음과 같습니다.
class TitleMeta(type):
@property
def title(self):
return getattr(self, '_title', 'Default Title')
@title.setter
def title(self, title):
self._title = title
# Do whatever else you want when the title is set...
이제 위에서 만든 메타 클래스를 사용하는 것을 제외하고는 원하는 실제 클래스를 정상으로 만듭니다.
# Python 2 style:
class ClassWithTitle(object):
__metaclass__ = TitleMeta
# The rest of your class definition...
# Python 3 style:
class ClassWithTitle(object, metaclass = TitleMeta):
# Your class definition...
위에서 설명한 것처럼 이 메타클래스를 정의하는 것은 우리가 단일 클래스에서만 사용할 수 있다면 좀 이상합니다.이 경우 Python 2 스타일을 사용하는 경우 클래스 본문 내에서 메타 클래스를 실제로 정의할 수 있습니다.그러면 모듈 범위에 정의되지 않습니다.
def _create_type(meta, name, attrs):
type_name = f'{name}Type'
type_attrs = {}
for k, v in attrs.items():
if type(v) is _ClassPropertyDescriptor:
type_attrs[k] = v
return type(type_name, (meta,), type_attrs)
class ClassPropertyType(type):
def __new__(meta, name, bases, attrs):
Type = _create_type(meta, name, attrs)
cls = super().__new__(meta, name, bases, attrs)
cls.__class__ = Type
return cls
class _ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, owner):
if self in obj.__dict__.values():
return self.fget(obj)
return self.fget(owner)
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
return self.fset(obj, value)
def setter(self, func):
self.fset = func
return self
def classproperty(func):
return _ClassPropertyDescriptor(func)
class Bar(metaclass=ClassPropertyType):
__bar = 1
@classproperty
def bar(cls):
return cls.__bar
@bar.setter
def bar(cls, value):
cls.__bar = value
bar = Bar()
assert Bar.bar==1
Bar.bar=2
assert bar.bar==2
nbar = Bar()
assert nbar.bar==2
저는 우연히 @Andrew와 매우 유사한 해결책을 생각해냈습니다. 오직 DRY뿐입니다.
class MetaFoo(type):
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.thingy})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
@property
def thingy(cls):
if not inspect.isclass(cls):
cls = type(cls)
return cls._thingy
@thingy.setter
def thingy(cls, value):
if not inspect.isclass(cls):
cls = type(cls)
cls._thingy = value
class Foo(metaclass=MetaFoo):
_thingy = 23
class Bar(Foo)
_thingy = 12
이것은 모든 답변 중 가장 좋은 것은 다음과 같습니다.
메타 속성이 클래스에 추가되어 인스턴스의 속성이 계속 됩니다.
- 어떤 클래스에서도 사물을 재정의할 필요가 없습니다.
- 이 속성은 인스턴스와 클래스 모두에서 "클래스 속성"으로 작동합니다.
- _thingy가 상속되는 방식을 사용자 지정할 수 있는 유연성이 있습니다.
제 경우에는, 실제로 사용자 정의했습니다._thingy
각 클래스에서 정의하지 않고(기본값 없이) 모든 자식에 대해 다음과 같이 다름:
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.services, '_thingy': None})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
게으른 로딩만 필요한 경우 클래스 초기화 방법을 사용할 수 있습니다.
EXAMPLE_SET = False
class Example(object):
@classmethod
def initclass(cls):
global EXAMPLE_SET
if EXAMPLE_SET: return
cls.the_I = 'ok'
EXAMPLE_SET = True
def __init__( self ):
Example.initclass()
self.an_i = 20
try:
print Example.the_I
except AttributeError:
print 'ok class not "loaded"'
foo = Example()
print foo.the_I
print Example.the_I
하지만 메타 클래스 접근 방식은 더 깨끗하고 예측 가능한 행동으로 보입니다.
아마도 당신이 찾고 있는 것은 싱글턴 디자인 패턴일 것입니다.Python에서 공유 상태를 구현하는 것에 대한 좋은 SOQA가 있습니다.
언급URL : https://stackoverflow.com/questions/5189699/how-to-make-a-class-property
'programing' 카테고리의 다른 글
Kotlin + Gradle 확인되지 않은 참조 (0) | 2023.06.26 |
---|---|
MongoDB 구조: 단일 컬렉션 대 여러 소규모 컬렉션 (0) | 2023.06.26 |
두 개의 선택 테이블을 결합하여 선택 쿼리에 실제 결과를 표시하는 것과 동일하지 않은 이유 (0) | 2023.06.26 |
인스턴스 간에 클래스 데이터가 공유되지 않도록 하려면 어떻게 해야 합니까? (0) | 2023.06.26 |
Numpy argsort - 그것은 무엇을 하고 있습니까? (0) | 2023.06.26 |