Python-传递对象值而不是引用
据我所知,在Python中,变量实际上是对给定名称空间中对象的引用。因此,在下面的示例中,当全局名称空间中的Python-传递对象值而不是引用,python,python-2.7,reference,Python,Python 2.7,Reference,据我所知,在Python中,变量实际上是对给定名称空间中对象的引用。因此,在下面的示例中,当全局名称空间中的noise更改时,cat.noise返回的值也会更改,因为setattr行中的引用使用的是noise的引用,而不是其基础值 class Cat(object): pass noise = "meow" setattr(Cat, "noise", property(lambda self: noise)) cat = Cat() cat.noise # Outputs "meo
noise
更改时,cat.noise
返回的值也会更改,因为setattr
行中的引用使用的是noise
的引用,而不是其基础值
class Cat(object):
pass
noise = "meow"
setattr(Cat, "noise", property(lambda self: noise))
cat = Cat()
cat.noise
# Outputs "meow"
noise = "purrrrr"
cat.noise
# Outputs "purrrrr"
也就是说,在如上所述调用setattr
时,是否有方法传递噪声值?我想我可以通过使用一个函数来隔离名称空间,这确实有效:
class Cat(object):
pass
noise = "meow"
def setproperties(cls, k, v):
setattr(cls, k, property(lambda self: v))
setproperties(Cat, "noise", noise)
cat = Cat()
cat.noise
# Outputs "meow"
noise = "purrrrr"
cat.noise
# Still outputs "meow"
是否可以在不通过函数传递对象的情况下执行此操作(不使用eval
等)?作为第二个问题,我关于引擎盖下发生的事情的推理正确吗
编辑
根据评论中的一个较少人为的例子的请求,请考虑下面的内容。想象一下,我正试图根据它的朋友狗的值,在Cat
中动态设置属性:
class Dog(object):
noise = 'woof'
adorable = True
class Cat(object):
friend = Dog
friend_attrs = filter(lambda attr: not attr.startswith('__'), Dog.__dict__)
for attr in friend_attrs:
setattr(Cat, "friend_" + attr, property(lambda self: getattr(self.friend, attr)))
cat = Cat()
cat.friend_noise
# Outputs True
cat.friend_adorable
# Outputs True
只需将噪声值传递给setattr
函数即可。例如
class C(object):
pass
noise = 'mrrrr'
setattr(C, 'noise', noise)
c = C()
c.noise
# outputs 'mrrrr'
noise = 'qwe'
c.noise
# outputs 'mrrrr'
编辑:用于出于某种原因需要getter函数的情况
您可以使用中间值
class D(object):
pass
noise = 'mrrrr'
setattr(D, '_noise', noise)
setattr(D, 'noise', property(lambda self: self._noise))
d = D()
d.noise
# Outputs 'mrrrr'
noise = 'pheew'
d.noise
# Outputs 'mrrrr'
编辑2:使用部分功能
import functools
class E(object):
pass
noise = 'mrrrr'
getter = lambda _, noise: noise
partial = functools.partial(getter, noise=noise)
setattr(E, 'noise', property(partial))
e = E()
e.noise
# Outputs 'mrrrr'
noise = 'fooooo'
e.noise
# Outputs 'mrrrr'
这是因为Python函数(包括lambda
s)使用符号绑定,即lambda
指向变量,而不是其值。要克服此问题,应将该变量括起来(创建闭包):
但是,您已经发现,通过使用函数来封装该操作,您自己已经做到了这一点。这是蟒蛇式的
setattr(Cat, "noise", property(lambda self: noise))
仅将属性定义为一个函数,该函数返回全局noise
变量的值。它类似于
def make_noise(self):
return noise
第二个案例稍微复杂一点。该属性现在返回v
的值,该值是在创建过程中定义的
我可以通过一个简单的函数产生相同的行为:
In [268]: bar = 'barstring'
In [269]: def foo():
...: return bar # returns the global
...:
In [270]: foo()
Out[270]: 'barstring'
In [271]: bar = 'xxx'
In [272]: foo()
Out[272]: 'xxx'
但是,如果我将bar
作为关键字传递,我可以将值锁定到当前值:
In [273]: def foo1(bar=bar):
...: return bar
...:
In [274]: foo1()
Out[274]: 'xxx'
In [275]: bar = 'ooo'
In [276]: foo1()
Out[276]: 'xxx'
In [277]: foo()
Out[277]: 'ooo'
但是如果我使条
可变(例如列表)
foo1
从其\uuuuu默认值\uuuuu
返回一个项(如果我不给它一个参数)foo
继续访问全局,因为bar
没有本地绑定
In [289]: foo1.__defaults__
Out[289]: (['two'],)
In [290]: foo.__defaults__
本质上,这是一个变量绑定到哪里的问题——局部绑定、在某个容器函数中绑定还是全局绑定
像这样的可变默认值可能很有用,但也可能导致错误
============
本地绑定的另一个示例
In [297]: def make_foo(bar):
...: def foo():
...: print(locals())
...: return bar
...: return foo
...:
In [298]: foo2=make_foo('test')
In [299]: foo2()
{'bar': 'test'}
Out[299]: 'test'
foo
看起来是一样的,但是现在bar
引用其局部变量中绑定的变量partial
和您的setproperties
做同样的事情。Python函数(包括lambda函数)按名称引用非局部变量。这意味着他们将获得该变量的最新值,而不是定义函数时的值(实际上,定义函数时根本不需要定义该变量)
解决此问题的一种方法是将该值作为函数参数的默认值。默认值是在函数定义时计算的,而不是在调用函数时计算的(这就是为什么可变默认参数经常有问题的原因)
试着这样做:
for attr in friend_attrs:
setattr(Cat, "friend_" + attr,
property(lambda self, attr=attr: # added attr=attr default here!
getattr(self.friend, attr))) # attr is the lambda's arg here
为什么要使用属性,而不是直接使用值本身?@Evert不幸的是,在我正在处理的实际示例中,它确实需要是属性,因为lambda函数更复杂。我知道上面的例子有点做作…“它确实需要是一个属性,因为lambda函数更复杂”-到底是什么问题?你能举一个不那么做作的例子吗?@ali\u我好的,几分钟后我会更新我的示例。不幸的是,这不是一个选项。这个例子有点做作,但在我正在处理的实际例子中,它确实需要是一个属性。添加了另一段代码,它使用中间值和属性函数。
In [297]: def make_foo(bar):
...: def foo():
...: print(locals())
...: return bar
...: return foo
...:
In [298]: foo2=make_foo('test')
In [299]: foo2()
{'bar': 'test'}
Out[299]: 'test'
for attr in friend_attrs:
setattr(Cat, "friend_" + attr,
property(lambda self, attr=attr: # added attr=attr default here!
getattr(self.friend, attr))) # attr is the lambda's arg here