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

据我所知,在Python中,变量实际上是对给定名称空间中对象的引用。因此,在下面的示例中,当全局名称空间中的
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