使用 @property
描述符可以自定义类变量的读取、写入和删除操作,而且可以在运行时间,新增或修改这样的类变量。但这样的 property
变量只能是类域下的变量,他的名字只存于对象所属类的 __dict__
中,因此如果你为某个实例对象使用 property
描述符,基于 Python 的调用规则是无法调用相关 get 或 set 函数的,我的问题是有任何方法能实现基于实例对象的动态 property
功能吗?感谢任何可能的建议或信息!
举例:
class A(object):
var = 2
我们现在有一个类,它有一个类域的变量 var
,所有根据类 A
生成的对象(实例)都可以访问这个变量
A.var = 3 或 print(A.var)
如果我创建一个函数,返回 var
的平方。
def power(self): return self.var**2
并把它以 property
的形式赋予类 A
:
A.power = property(power)
那么现在可以通过访问类实例的power属性a.power
来获得 var
的平方:
a=A()
>>> a.power
9
所有类的实例都能访问 var
和 power
两个属性,这样我就动态的为类 A
加了一个属性,只读的。
但如果为某个 A
类对象实例增加 property
:
a = A()
def super(self):
return self.var**3
a.super = property(super)
再执行:
>>> a.super
<property at 0x1039fb778>
仅仅是返回一个 property
对象,而不会真正调用。这就是不能为类的实例增加 property
属性。
手机打字,不太方便,还请谅解
一个是
A.power = property(power)
a.power
9
另一个是
a.super = property(super)
a.super
<property 0x1039fb778>
前一个成立,后一个不行
前面有点错误,感谢doukelung指出,已修改。
前面提问是在手机上写的,可能没讲清楚,回来专门重新做了个例子:
# 这里以一个记录有关教师和学生信息的类为例:
# 类中保存的信息有四项:姓名、身份、出生日期、学号/教职工编号、其中学号的内容是根据出生日期加
# 前缀2015生成的,教职工编号则根据出生日期加J2115前缀生成。出于演示的目的,姓名我使用实例变量的形式。身份和出生日期我使用类变量的形式,而编号则用property属性的方式来实现。
# 以下是代码:
class person(object):
position = None # 字符串如'teacher/student'
birthday = None # 字符串如'19900512',1990年5月12日
def __init__(self,name,position,birthday):
self.name = name # 实例变量
self.position = position # 也是实例变量,但是在类中定义的,所以可以在
self.birthday = birthday # person.__dict__中查到,当然每个实例中都
# 有一个独立的copy。
@property # property 属性
def id(self):
if self.position == 'teacher': return 'J2115'+self.birthday
if self.position == 'student': return '2015'+self.birthday
tom = person('tom','student','19990321')
jim = person('jim ','teacher','19770802')
print('name:%s\nposition:%s\nbirthday:%s\nid:%s'%(tom.name,tom.position,
tom.birthday,tom.id))
print('name:%s\nposition:%s\nbirthday:%s\nid:%s'%(jim.name,jim.position,
jim.birthday,jim.id))
#######################################################################
# 运行结果:
name:tom
position:student
birthday:19990321
id:201519990321
name:jim
position:teacher
birthday:19770802
id:J211519770802
# 如果我为person增加一个property属性,那么所有实例都能访问这个新的property属性
def older(self):
return True if int(self.birthday[0:4]) < 1980 else False
person.older = property(older)
>>>tom.older
False
>>>jim.older
True
# 如果我希望为tom增加一个此对象独有的变量,只能用基本的实例变量形式
>>>tom.livein = 'Beijing'
>>>tom.livein
Beijing
>>>jim.livein
AttributeError: 'person' object has no attribute 'livein'
# 对象jim并没有livein这个变量
# 如果我以property的形式为tom增加一个属性
def sex(self):
return 'female' if self.name in ['mary','linda','jean'] else 'male'
tom.sex = property(sex)
>>>tom.sex
<property at 0x103a07728>
# 它并不会执行sex函数,这就是说property只能赋予类域才起效果
# 我的问题就是有没有办法让它对实例对象也能有效
阿神2017-04-18 09:18:01
首先,建议题主看一下python描述其引导这一章节:http://pyzh.readthedocs.io/en...
“默认对属性的访问控制是从对象的字典里面(__dict__)中获取(get), 设置(set)和删除(delete)它。举例来说, a.x 的查找顺序是, a.__dict__['x'] , 然后 type(a).__dict__['x'] , 然后找 type(a) 的父类(不包括元类(metaclass)).”
在A实例化之后:
a.super = property(super)
dir(a)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_x', 'getx', 'setx', 'super', 'x']
a.__dict__
{'super': <property object at 0x1092ece68>, '_x': 0}
请看a的__dict__中包含了什么,在a.super时,首先查找的是__dict__中是否包含了super,如果有则优先返回字典中的对象,也就是property。
ps:能告诉我你想实现基于类实例的动态property的目的吗? 因为类实例的对象你可以随意修改啊,为什么还需要property这个属性去做?
黄舟2017-04-18 09:18:01
是的。
首先明确一点,给类增加property成员x时,实例中并没有x,
当你调用实例成员变量时,才调用类的property成员去计算值,
而直接给实例添加property成员的话,调用成员变量则是直接访问property成员。
说起来……如果两个实例可访问的成员不同……这其实就是两个类啊……
硬要实现的话,可以动态的在实例中增加一个变量__insproperty__,把属性加到这个变量里,然后实现/装饰类中的__getattr__方法,在__insproperty__查找,找到的话调用该属性的__get__方法……