在python中,我可以使用@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
我上面使用的语法是可能的,还是需要更多的语法?
我想要类属性的原因是我可以延迟加载类属性,这看起来很合理。
发布于 2011-03-04 16:12:13
下面是我如何做到这一点:
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)
现在一切都会好起来的。
发布于 2011-03-04 18:13:47
如果您按如下方式定义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
将覆盖属性对象本身。
发布于 2011-03-04 12:44:16
我认为您可以使用元类来做到这一点。因为元类可以类似于类的类(如果这有意义的话)。我知道您可以为元类分配一个__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__
,其余都是一样的。
https://stackoverflow.com/questions/5189699
复制相似问题