使用 @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只能赋予类域才起效果
# 我的问题就是有没有办法让它对实例对象也能有效
首先,建議題主看一下python描述其引導這一章節:http://pyzh.readthedocs.io/en...
「預設屬性的存取控制是從物件的字典裡面(__dict__)取得(get), 設定(set)和刪除(delete)它。舉例來說, a.x 的查找順序是, a.__dict__[' x'] , 然後type(a).__dict__['x'] , 然後找type(a) 的父類(不包括元類(metaclass)).」
在A實例化之後:, '_x': 0}
a.super = property(super)
dir(a)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__delgetattribute', '__getattribute___' __hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_xx' , 'getx', 'setx', 'super', 'x']
a.__dict__
{'super':
請看a的__dict__包含了什麼,在a.super時,首先查找的是__dict__中是否包含了super,如果有則優先返回字典中的對象,也就是property。
ps:能告訴我你想實現基於類別實例的動態property的目的嗎? 因為類別實例的物件你可以隨意修改啊,為什麼還需要property這個屬性去做?
是的。
首先明確一點,給類別增加property成員x時,實例中並沒有x,
當你呼叫實例成員變數時,才調用類別的property成員去計算值,
而直接給實例添加property成員的話,調用成員變數則是直接存取property成員。
說起來…如果兩個實例可存取的成員不一樣…這其實就是兩個類別啊…
硬要實現的話,可以動態的在實例中增加一個變數__insproperty__,把屬性加到這個變數裡,然後實現/裝飾類別中的__getattr__方法,在__insproperty__查找,找到的話調用該屬性的__get__方法…