Python 在类方法上使用property()
我有一个包含两个类方法的类(使用Python 在类方法上使用property(),python,oop,Python,Oop,我有一个包含两个类方法的类(使用classmethod()函数),用于获取和设置本质上是静态变量的内容。我试图将property()函数与这些函数一起使用,但它导致了一个错误。我能够在解释器中再现以下错误: class Foo(object): _var = 5 @classmethod def getvar(cls): return cls._var @classmethod def setvar(cls, value):
classmethod()
函数),用于获取和设置本质上是静态变量的内容。我试图将property()
函数与这些函数一起使用,但它导致了一个错误。我能够在解释器中再现以下错误:
class Foo(object):
_var = 5
@classmethod
def getvar(cls):
return cls._var
@classmethod
def setvar(cls, value):
cls._var = value
var = property(getvar, setvar)
我可以演示类方法,但它们不能用作属性:
>>> f = Foo()
>>> f.getvar()
5
>>> f.setvar(4)
>>> f.getvar()
4
>>> f.var
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
>>> f.var=5
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
>f=Foo()
>>>f.getvar()
5.
>>>f.setvar(4)
>>>f.getvar()
4.
>>>f.var
回溯(最近一次呼叫最后一次):
文件“”,第1行,是否在中?
TypeError:“classmethod”对象不可调用
>>>f.var=5
回溯(最近一次呼叫最后一次):
文件“”,第1行,是否在中?
TypeError:“classmethod”对象不可调用
是否可以将
property()
函数与@classmethod
修饰函数一起使用?以下是我的建议。不要使用类方法
真的
在这种情况下使用类方法的原因是什么?为什么没有一个普通类的普通对象呢
如果您只是想更改值,那么属性并不是很有用,是吗?只需设置属性值就可以了 只有在有需要隐藏的内容时才应该使用属性,这些内容可能在将来的实现中发生更改 也许你的例子很简单,还有一些地狱般的计算。但它看起来并没有带来显著的价值 受Java影响的“隐私”技术(在Python中,属性名称以_开头)实际上没有多大帮助。谁的私人信件?当您拥有源代码时,private的意义有点模糊(就像在Python中一样)
受Java影响的EJB风格的getter和setter(通常在Python中作为属性完成)用于促进Java的基本自省,以及通过静态语言编译器。在Python中,所有这些getter和setter都没有那么有用。半个解决方案是,类上的set仍然不起作用。该解决方案是一个自定义属性类,同时实现属性和staticmethod
class ClassProperty(object):
def __init__(self, fget, fset):
self.fget = fget
self.fset = fset
def __get__(self, instance, owner):
return self.fget()
def __set__(self, instance, value):
self.fset(value)
class Foo(object):
_bar = 1
def get_bar():
print 'getting'
return Foo._bar
def set_bar(value):
print 'setting'
Foo._bar = value
bar = ClassProperty(get_bar, set_bar)
f = Foo()
#__get__ works
f.bar
Foo.bar
f.bar = 2
Foo.bar = 3 #__set__ does not
没有合理的方法使这个“类属性”系统在Python中工作 这里有一个不合理的方法让它发挥作用。随着元类魔法数量的增加,你当然可以使它更加无缝
class ClassProperty(object):
def __init__(self, getter, setter):
self.getter = getter
self.setter = setter
def __get__(self, cls, owner):
return getattr(cls, self.getter)()
def __set__(self, cls, value):
getattr(cls, self.setter)(value)
class MetaFoo(type):
var = ClassProperty('getvar', 'setvar')
class Foo(object):
__metaclass__ = MetaFoo
_var = 5
@classmethod
def getvar(cls):
print "Getting var =", cls._var
return cls._var
@classmethod
def setvar(cls, value):
print "Setting var =", value
cls._var = value
x = Foo.var
print "Foo.var = ", x
Foo.var = 42
x = Foo.var
print "Foo.var = ", x
问题的症结在于属性是Python所称的“描述符”。没有简单明了的方法来解释这种元编程是如何工作的,所以我必须向您指出
只有在实现一个相当高级的框架时,才需要理解这类事情。比如透明对象持久化或RPC系统,或者一种特定于域的语言
然而,在对之前答案的评论中,你说你
需要修改一个属性,该属性以一个类的所有实例都可以看到的方式,并且在调用这些类方法的作用域中没有对该类的所有实例的引用
在我看来,你真正想要的是一个设计模式
因为我需要修改一个属性,该属性可以被类的所有实例看到,并且在调用这些类方法的作用域中没有对类的所有实例的引用 您是否至少有权访问该类的一个实例?我可以想出一个办法:
class MyClass (object):
__var = None
def _set_var (self, value):
type (self).__var = value
def _get_var (self):
return self.__var
var = property (_get_var, _set_var)
a = MyClass ()
b = MyClass ()
a.var = "foo"
print b.var
尝试一下,它可以在不必更改/添加大量现有代码的情况下完成任务
>>> class foo(object):
... _var = 5
... def getvar(cls):
... return cls._var
... getvar = classmethod(getvar)
... def setvar(cls, value):
... cls._var = value
... setvar = classmethod(setvar)
... var = property(lambda self: self.getvar(), lambda self, val: self.setvar(val))
...
>>> f = foo()
>>> f.var
5
>>> f.var = 3
>>> f.var
3
属性
函数需要两个可调用的
参数。给他们lambda包装(它将实例作为第一个参数传递给他们),一切都很好。阅读注释,我发现如下内容
在以下情况下不会调用[属性的]get方法:
该属性作为类进行访问
属性(C.x),而不是作为
实例属性(C().x)。如果你
要覆盖uuu get uuu操作吗
用于用作类时的属性
属性,则可以将属性子类化-
它本身就是一种新型的
扩展它的_uget _uu方法,或者您可以
从头定义描述符类型
通过创建一个新样式的类
定义获取、设置和
__删除方法
注意:下面的方法实际上不适用于setter,只适用于getter。
因此,我认为规定的解决方案是创建一个ClassProperty作为property的子类
class ClassProperty(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()
class foo(object):
_var=5
def getvar(cls):
return cls._var
getvar=classmethod(getvar)
def setvar(cls,value):
cls._var=value
setvar=classmethod(setvar)
var=ClassProperty(getvar,setvar)
assert foo.getvar() == 5
foo.setvar(4)
assert foo.getvar() == 4
assert foo.var == 4
foo.var = 3
assert foo.var == 3
但是,二传手实际上不起作用:
foo.var = 4
assert foo.var == foo._var # raises AssertionError
foo.\u var
保持不变,只需使用新值覆盖属性即可
您还可以使用ClassProperty
作为装饰器:
class foo(object):
_var = 5
@ClassProperty
@classmethod
def var(cls):
return cls._var
@var.setter
@classmethod
def var(cls, value):
cls._var = value
assert foo.var == 5
属性是在类上创建的,但会影响实例。因此,如果需要classmethod属性,请在元类上创建该属性
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... pass
... @classmethod
... def getvar(cls):
... return cls._var
... @classmethod
... def setvar(cls, value):
... cls._var = value
...
>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
In [1]: class ClassPropertyMeta(type):
...: @property
...: def prop(cls):
...: return cls._prop
...: def __new__(cls, name, parents, dct):
...: # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
...: dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
...: dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
...: return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
...:
In [2]: class ClassProperty(object):
...: __metaclass__ = ClassPropertyMeta
...: _prop = 42
...: def __getattr__(self, attr):
...: raise Exception('Never gets called')
...:
In [3]: ClassProperty.prop
Out[3]: 42
In [4]: ClassProperty.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-e2e8b423818a> in <module>()
----> 1 ClassProperty.prop = 1
AttributeError: can't set attribute
In [5]: cp = ClassProperty()
In [6]: cp.prop
Out[6]: 42
In [7]: cp.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-e8284a3ee950> in <module>()
----> 1 cp.prop = 1
<ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val)
6 # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
7 dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
----> 8 dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
9 return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
AttributeError: can't set attribute
但是,既然您使用的是元类,那么只要将classmethods移到元类中,它的可读性就会更好
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
或者,使用Python 3的元类=…
语法,以及在foo
类主体之外定义的元类,以及负责设置\u var
初始值的元类:
>>> class foo_meta(type):
... def __init__(cls, *args, **kwargs):
... cls._var = 5
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> class foo(metaclass=foo_meta):
... pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
如果您希望通过实例化对象访问class属性,那么仅在meta类上设置它是没有帮助的,在这种情况下,您还需要在对象上安装一个普通属性(它将分派给class属性)。我认为以下几点更为明确:
#!/usr/bin/python
class classproperty(property):
def __get__(self, obj, type_):
return self.fget.__get__(None, type_)()
def __set__(self, obj, value):
cls = type(obj)
return self.fset.__get__(None, cls)(value)
class A (object):
_foo = 1
@classproperty
@classmethod
def foo(cls):
return cls._foo
@foo.setter
@classmethod
def foo(cls, value):
cls.foo = value
a = A()
print a.foo
b = A()
print b.foo
b.foo = 5
print a.foo
A.foo = 10
print b.foo
print A.foo
我希望这个死气沉沉的简单只读
@classproperty
装饰程序能够帮助寻找classproperty的人
class classproperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class C(object):
@classproperty
def x(cls):
return 1
assert C.x == 1
assert C().x == 1
可以将property()函数与classmethod修饰函数一起使用吗?
没有
然而,classmethod只是一个绑定方法(部分函数),它位于一个可从该类的实例访问的类上
由于实例是类的函数,并且可以从实例派生类,因此可以使用ex1 = Example()
ex2 = Example()
ex1.class_property = None
ex2.class_property = 'Example'
assert ex1.class_property is ex2.class_property
del ex2.class_property
assert not hasattr(ex1, 'class_property')
class classproperty(property):
def __get__(self, obj, objtype=None):
return super(classproperty, self).__get__(objtype)
def __set__(self, obj, value):
super(classproperty, self).__set__(type(obj), value)
def __delete__(self, obj):
super(classproperty, self).__delete__(type(obj))
class Foo(object):
_bar = 5
@classproperty
def bar(cls):
"""this is the bar attribute - each subclass of Foo gets its own.
Lookups should follow the method resolution order.
"""
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
@bar.deleter
def bar(cls):
del cls._bar
def main():
f = Foo()
print(f.bar)
f.bar = 4
print(f.bar)
del f.bar
try:
f.bar
except AttributeError:
pass
else:
raise RuntimeError('f.bar must have worked - inconceivable!')
help(f) # includes the Foo.bar help.
f.bar = 5
class Bar(Foo):
"a subclass of Foo, nothing more"
help(Bar) # includes the Foo.bar help!
b = Bar()
b.bar = 'baz'
print(b.bar) # prints baz
del b.bar
print(b.bar) # prints 5 - looked up from Foo!
if __name__ == '__main__':
main()
class MetaWithFooClassProperty(type):
@property
def foo(cls):
"""The foo property is a function of the class -
in this case, the trivial case of the identity function.
"""
return cls
class FooClassProperty(metaclass=MetaWithFooClassProperty):
@property
def foo(self):
"""access the class's property"""
return type(self).foo
>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>
>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>
In [1]: class ClassPropertyMeta(type):
...: @property
...: def prop(cls):
...: return cls._prop
...: def __new__(cls, name, parents, dct):
...: # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
...: dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
...: dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
...: return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
...:
In [2]: class ClassProperty(object):
...: __metaclass__ = ClassPropertyMeta
...: _prop = 42
...: def __getattr__(self, attr):
...: raise Exception('Never gets called')
...:
In [3]: ClassProperty.prop
Out[3]: 42
In [4]: ClassProperty.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-e2e8b423818a> in <module>()
----> 1 ClassProperty.prop = 1
AttributeError: can't set attribute
In [5]: cp = ClassProperty()
In [6]: cp.prop
Out[6]: 42
In [7]: cp.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-e8284a3ee950> in <module>()
----> 1 cp.prop = 1
<ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val)
6 # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
7 dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
----> 8 dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
9 return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
AttributeError: can't set attribute
from future.utils import with_metaclass
class BuilderMetaClass(type):
@property
def load_namespaces(self):
return (self.__sourcepath__)
class BuilderMixin(with_metaclass(BuilderMetaClass, object)):
__sourcepath__ = 'sp'
print(BuilderMixin.load_namespaces)
class FooProperties(type):
@property
def var(cls):
return cls._var
class Foo(object, metaclass=FooProperties):
_var = 'FOO!'
class class_property(object):
# this caches the result of the function call for fn with cls input
# use this as a decorator on function methods that you want converted
# into cached properties
def __init__(self, fn):
self._fn_name = fn.__name__
if not isinstance(fn, (classmethod, staticmethod)):
fn = classmethod(fn)
self._fn = fn
def __get__(self, obj, cls=None):
if cls is None:
cls = type(obj)
if (
self._fn_name in vars(cls) and
type(vars(cls)[self._fn_name]).__name__ != "class_property"
):
return vars(cls)[self._fn_name]
else:
value = self._fn.__get__(obj, cls)()
setattr(cls, self._fn_name, value)
return value