使用实例级别而非类级别定义的属性
我想实现的功能大概是这样的:
class object:
def __init__(self):
WidthVariable(self)
print self.width
#Imagine I did this 60frames/1second later
print self.width
#output:
>>0
>>25
我希望发生的事情(如上所述):当创建一个名为 WidthVariable
的类时,它会将一个变量 width
添加到对象 instance 中。这个变量就像普通的属性,但在这个特定的情况下,它是只读的(只有 fget
变量被设置)。此外,fget
会调用一个在 WidthVariable
中定义的函数,这个函数决定返回什么样的 width
。
不过,我不知道该怎么做!我尝试使用普通属性,但发现它们只在类上有效,而不是在每个实例上——请注意,我使用的代码应该尽可能与上面的代码相似(也就是说,只有 WidthVariable
的 __init__
方法中的代码应该设置 width
变量,其他地方都不可以)。另外,self.width
不能是一个函数,因为我不想像 self.width()
这样调用它,我想要的是 self.width
(因为这与我设计的其他部分保持一致)。
为了更清楚,完整的代码大概是这样的:
class MyObject:
def __init__(self)
WidthVariable(self)
print self.width
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
def get_width(self):
value = #do_stuff
return value #The Value
#output:
>>25 #Whatever the Value was
4 个回答
我不太明白你这个例子的构造方式,也不太理解你说的“普通属性”只能在“类”上工作的意思。下面是如何创建一个只读属性的示例:
class Foo(object):
# create read-only property "rop"
rop = property(lambda self: self._x)
def __init__(self):
self._x = 0
def tick(self):
self._x += 1
f = Foo()
print f.rop # prints 0
f.tick()
f.tick()
print f.rop # prints 2
f.rop = 4 # this will raise AtributeError
我觉得我现在明白你的问题了,不过我也觉得你可能有点不幸。
对于新式类,特殊方法的隐式调用只有在定义在对象的类型上时才能正确工作,而不能在对象的实例字典里。
描述符(用来实现属性的东西)必须出现在类的 __dict__
中,而不能出现在实例的 __dict__
中。换句话说,Python和Ruby是不一样的!
我很期待有大神的Python元编程高手来纠正我,但我觉得我说的没错。
正如@Jonathan所说,描述符(包括属性)是针对类的,而不是针对实例的。所以,要让每个实例有不同的描述符,唯一的方法就是让每个实例自己定义自己的类。这其实很简单,算是元编程中的小事儿;-)...:
class Individualistic(object_or_whatever_bases):
def __init__(self, whatever_args):
self.__class__ = type('GottaBeMe', (self.__class__, object), {})
# keep rocking...!-)
我还特别提到要使用object
,因为在Python 2.*
中,这是必要的,你也说你在用这个版本!!! 以后不要再用旧式类了,它们在属性等方面的表现不太正常(而且为了向后兼容,它们也做不到这一点——在Python 3中,旧式类终于被淘汰了,所以现在每个类都是新式类,不再需要显式地继承object
了!)。
现在,放在self.__class__.__dict__
中的任何描述符只会影响这个特定的实例,不会影响其他实例。虽然会有一点额外开销(每个GottaBeMe
类和每个实例都有自己的__dict__
等),但也没什么太糟糕的。
现在,要满足最初的要求,只需要改动一下:
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
(同时合理地重命名object
参数,以免覆盖内置名称,并且让类变成新式类,因为你应该总是让每个类都是新式的;-),改成:
class WidthVariable(object):
def __init__(self, obj)
obj.__class__.width = property(self.get_width)
看起来并没有什么神秘的东西,对吧!-)