programing

클래스 속성을 만드는 방법?

lastmoon 2023. 6. 26. 21:36
반응형

클래스 속성을 만드는 방법?

할 수 .@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

이것은 모든 답변 중 가장 좋은 것은 다음과 같습니다.

메타 속성이 클래스에 추가되어 인스턴스의 속성이 계속 됩니다.

  1. 어떤 클래스에서도 사물을 재정의할 필요가 없습니다.
  2. 이 속성은 인스턴스와 클래스 모두에서 "클래스 속성"으로 작동합니다.
  3. _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

반응형