十个Python陷阱(4-7)

4. 类属性和实例属性

这边至少有两个小陷阱。第一,新手习惯于把属性放在类里面(而不是实例中),他们当看到这些属性会在实例之间共享的时候会很惊讶:

>>> class Foo:
...     bar = []
...     def __init__(self, x):
...         self.bar.append(x)
...     
>>> f = Foo(42)
>>> g = Foo(100)
>>> f.bar, g.bar
([42, 100], [42, 100])
这不是语言的缺陷,而是非常好的特性,在很多情形下非常有用。造成误解的原因是你使用的是类的属性而不是实例的属性,这两个是有区别的,Python中创建实例的属性和其他语言有区别,在C++或Object Pascal中,需要在class body中定义属性。

另一个小陷阱:self.foo可能指的是实例属性foo,或者在没有实例属性的情况下,指类属性foo。也就是说:在属性名称相同的情况下,实例属性会覆盖类属性。如下:

>>> class Foo:
...     a = 42
...     def __init__(self):
...         self.a = 43
...     
>>> f = Foo()
>>> f.a
43
>>> class Foo:
...     a = 42
...     
>>> f = Foo()
>>> f.a
42
第一个例子中,f.a指实例属性,值为43。它覆盖了值为42的类属性。第二个例子中,没有实例属性,所以f.a值类属性。

下面的代码将两者结合起来:

class foo:
	bar = []
	def __init__(self, x):
		print(id(self.bar))
		self.bar = self.bar + [x]
		print(id(self.bar))

f = foo(1)
print(f.bar)
g = foo(2)
print(g.bar)
prints:
33254536
33257672
[1]
33254536
33251528
[2]

self.bar = self.bar  + [x]中,两个self.bar并不相同。第二个指的是类属性bar,而表达式的结果却赋给了实例属性。从属性的id上我们可以清楚的看到。

解决方法:这之间的区别可能会让人迷惑,但是却很好理解。当你想要在各个类实例之间公用某些东西时,使用类属性。为了避免混淆,你可以使用self.__class__.name来代替self.name,即使没有同名属性。当某属性对于某个实例是独一无二时,使用实例属性,并用self.name表示。

下面是一个更令人困惑的例子:

>>> class Foo:
...	bar = []
...	def __init__(self, x):
...		self.bar += [x]
...		
>>> f = Foo(42)
>>> g = Foo(100)
>>> f.bar
[42, 100]
>>> g.bar
[42, 100]

读者自己去体会为什么会这样?(参看上一篇的陷阱#3)


5. 可变的默认参数

这个陷阱是上一篇#2的变种:

>>> def popo(x=[]):
...     x.append(666)
...     print x
...     
>>> popo([1, 2, 3])
[1, 2, 3, 666]
>>> x = [1, 2]
>>> popo(x)
[1, 2, 666]
>>> x
[1, 2, 666]
这个是预期的,但是下面的:
>>> popo()
[666]
>>> popo()
[666, 666]
>>> popo()
[666, 666, 666]
可能你觉得不管函数执行多少次,输出都应该是[666],因为popo()执行时默认参数为x = []。错!默认参数只绑定“一次”,是当函数被创建的时候,而不是函数被调用的时候。所以,如果是一个可变对象,每一次调用都会改变它(默认参数时)。

解决方法:这个行为很少会有用处,你只要稍作注意即可。


6. UnboundLocalError

>>> def p():
...     x = x + 2
...     
>>> p()
Traceback (most recent call last):
  File "<input>", line 1, in ?
  File "<input>", line 2, in p
UnboundLocalError: local variable 'x' referenced before 
assignment
在函数p中,x = x + 2不能被解析,因为x还没有值,再来看看如下代码:
>>> x = 2
>>> def q():
...     print x
...     x = 3
...     print x
...     
>>> q()
Traceback (most recent call last):
  File "<input>", line 1, in ?
  File "<input>", line 2, in q
UnboundLocalError: local variable 'x' referenced before 
assignment

换句话说:一个变量可以使global的或者local的,但不能既是global又是local。


7. 个人觉得这不算个陷阱。。。所以就不翻译了。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值