首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何创建一个类属性?

如何创建一个类属性?
EN

Stack Overflow用户
提问于 2011-03-04 12:34:01
回答 7查看 207.1K关注 0票数 169

在python中,我可以使用@classmethod装饰器将方法添加到类中。是否有类似的装饰器将属性添加到类中?我能更好地展示我在说什么。

代码语言:javascript
复制
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

我上面使用的语法是可能的,还是需要更多的语法?

我想要类属性的原因是我可以延迟加载类属性,这看起来很合理。

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2011-03-04 16:12:13

下面是我如何做到这一点:

代码语言:javascript
复制
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__

添加元类定义可以解决以下问题:

代码语言:javascript
复制
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)

现在一切都会好起来的。

票数 114
EN

Stack Overflow用户

发布于 2011-03-04 18:13:47

如果您按如下方式定义classproperty,那么您的示例将完全按照您的请求工作。

代码语言:javascript
复制
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将覆盖属性对象本身。

票数 53
EN

Stack Overflow用户

发布于 2011-03-04 12:44:16

我认为您可以使用元类来做到这一点。因为元类可以类似于类的类(如果这有意义的话)。我知道您可以为元类分配一个__call__()方法来覆盖对MyClass()类的调用。我想知道在元类上使用property装饰器是否有类似的操作。(我以前没有尝试过,但现在我很好奇...)

更新:

哇,它确实起作用了:

代码语言:javascript
复制
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__,其余都是一样的。

票数 28
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5189699

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档