使用 @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只能赋予类域才起效果
# 我的问题就是有没有办法让它对实例对象也能有效
Tout d'abord, il est recommandé au sujet de jeter un oeil au chapitre décrivant le guidage de python : http://pyzh.readthedocs.io/en...
"Le contrôle d'accès par défaut pour les attributs consiste à les obtenir (obtenir), à définir (définir) et à supprimer (supprimer) du dictionnaire de l'objet (__dict__). Par exemple, l'ordre de recherche de a.x est a . __dict__['x'], puis tapez(a).__dict__['x'], puis recherchez la classe parent de type(a) (à l'exclusion de la métaclasse)."
Après l'instanciation de 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' : <objet de propriété à 0x1092ece68> '_x' : 0}
Veuillez voir ce qui est contenu dans __dict__ de a. Lorsque a.super est utilisé, la première chose à vérifier est si __dict__ contient super. Si c'est le cas, le dictionnaire sera renvoyé en premier. L'objet est la propriété.
ps : Pouvez-vous me dire ce que vous souhaitez réaliser avec des propriétés dynamiques basées sur des instances de classe ? Parce que vous pouvez modifier l'objet de l'instance de classe à volonté, pourquoi avez-vous besoin de l'attribut property pour le faire ?
Oui.
En parlant de ça... si les membres accessibles de deux instances sont différents... ce sont en fait deux classes...Tout d'abord, précisez que lorsque vous ajoutez un membre de propriété x à une classe, il n'y a pas de x dans l'instance
Lorsque vous appelez la variable membre d'instance, le membre de propriété de la classe. est appelée pour calculer la valeur. Si vous ajoutez un membre de propriété à une instance, l'appel de la variable membre accède directement au membre de propriété.