知乎上看到几个题,发现自己掌握的不好,记录一下警示下自己。
Python函数参数默认值的陷阱
1 2 3 4 5 6
| def f(x=[]): x.append(3) print(len(x)) f() f()
|
解释:
如果参数的默认值是一个不可变(Imuttable)数值,那么在函数体内如果修改了该参数,那么参数就会重新指向另一个新的不可变值。而如果参数默认值是与上述方法一样,是一个可变对象(Muttable),那么情况就比较糟糕了。所有函数体内对于该参数的修改,实际上都是对compile阶段就已经确定的那个对象的修改。
可变类型(Muttable):列表,字典
不可变类型(Imuttable):数字,字符串,元组
全局变量的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| x = 2 def f(): print(x)
def g(): print(x) x = 3 def j(): global x x = 3 print(x) def h(): exec('x = 4') print(x)
try: f() except: print('Oops') try: g() except: print('Oops') try: j() except: print('Oops') try: h() except: print('Oops')
|
解释:
第一个try:函数内部可以访问全局变量。
第二个try:函数内部不能直接为全局变量赋值。
第三个try:使用global关键词,可以在函数内部访问并修改全局变量的值。
第四个try:可以看出exec的作用域是全局的,具体细节也不是很清楚。
python多继承顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class M(type): def __init__(cls, name, bases, attrs, **kwargs): super().__init__(name, bases, attrs, **kwargs) def f(self): try: super(cls, self).f() except AttributeError: pass print(name) cls.f = f
class A(metaclass=M): pass
class AA(A): pass
class AAA(AA): pass
class B(metaclass=M): pass
class C(AAA, B, metaclass=M): pass
C().f() print(C.__mro__)
|
1 2 3 4 5 6
| B A AA AAA C (<class '__main__.C'>, <class '__main__.AAA'>, <class '__main__.AA'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
|
解释:
在python多继承中,如果每个类下有同名方法,那个调用的顺序是按照python的mro算法顺序来执行的,这里我们打印了C()的mro顺序 发现是 B, A, AA, AAA, C(对MRO算法有兴趣的可以自己去查阅相关资料). 在f方法里,会调用父类的f方法,因此会层层调用每个类的f方法,在递归里,输出语句在递归调用之后,则最先运行的最后输出。因为不难得出结果就是mro的反顺序。
python类变量和实例变量
1 2 3 4 5 6 7 8
| class A: x = 2 def f(self, y): print(A.x, self.x) self.x = self.x + y print(A.x, self.x)
A().f(3)
|
解释:
在python里,不管是实例还是类本身,都有一个自己的__dict__属性,它的值为一个字典, key为属性名,value为属性值。在访问一个实例的属性时,查找顺序为优先查找自己的__dict__属性,然后是类,然后是父类。
第一个print,此时实例本身并没有x属性,所以A.x和self.x都读到了类属性中的x,所以都是2
第二个print,在这之前,self.x即实例的x属性已经被创建且赋值,存在了自身的__dict__属性中。