Python的四个代码输出问题

Yang 317

Python是一种非常流行的编程语言,它具有许多优点,但它的一些缺点,让人在使用过程中常常感觉很绝望,其中最让我感觉绝望的是在学习python过程中遇到的四个代码输出问题。

一、以下代码的输出是什么

问题1

class Parent(object):

    x = 1

class Child1(Parent):

    pass

class Child2(Parent):

    pass

print Parent.x, Child1.x, Child2.x

Child1.x = 2

print Parent.x, Child1.x, Child2.x

Parent.x = 3

print Parent.x, Child1.x, Child2.x

答案

以上代码的输出是

1 1 1

1 2 1

3 2 3

最后一行的输出是 3 2 3 而不是 3 2 1。为什么改变了 Parent.x 的值还会改变 Child2.x 的值,但是 Child1.x 值却没有改变?

在Python 中,类变量在内部是作为字典处理的。如果一个变量的名字没有在当前类的字典中发现,将按照MRO在其父类中查找,直到被引用的变量被找到(如果这个被引用的变量名既没有在自己所在的类又没有在父类中找到,会引发 AttributeError 异常 )。

因此,名列前茅个 print 语句的输出是 1 1 1;

如果任何它的子类重写了该值(如执行语句 Child1.x = 2),该值仅在子类中被改变(相当于给Child1添加了名为’x’的类属性)。这就是为什么第二个 print 语句的输出是 1 2 1;

如果该值在父类中被改变(如执行语句 Parent.x = 3),这个改变会影响到任何未重写该值的子类(在这个示例中被影响的子类是 Child2)。这就是为什么第三个 print 输出是 3 2 3。

问题2

list = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]

print list[10:]

答案: [],并且不会触发IndexError异常。

当访问一个超过列表索引值的成员将导致 IndexError(如访问上述列表的 list[10])。但是,试图访问一个列表的以超出列表成员数作为开始索引的切片将不会导致 IndexError,仅会返回一个空列表。

问题3

def multipliers():

    return [lambda x : i * x for i in range(4)]

print [m(2) for m in multipliers()]

答案

以上代码的输出是 [6, 6, 6, 6] (而不是 [0, 2, 4, 6])。

原因是 Python 的闭包的后期绑定导致的 late binding,这意味着在闭包中的变量是在内部函数被调用的时候被查找。当multipliers() 返回的函数被调用,i 的值是在被调用时的作用域中查找,到那时,for 循环已经完成,i 最后的值是 3,因此,每个返回的函数中i的值都是 3。

(顺便说下,正如在 The Hitchhiker’s Guide to Python 中指出的,一个 lambda 表达式创建的函数不是特殊的,和使用一个普通的 def 创建的函数是一样的。)

这里有两种方法解决这个问题。

最普遍的解决方案是创建一个闭包,通过使用默认参数立即绑定它的参数。如:

def multipliers():

    return [lambda x, i=i : i * x for i in range(4)]

另外一个选择是,你可以使用 functools.partial 函数:

”’这个有点麻烦- -”’

from functools import partial

from operator import mul

def multipliers():

    return [partial(mul, i) for i in range(4)]

问题4

尽量不要把可变类型设为默认参数…

def extendList(val, list_var=[]):

    list_var.append(val)

    return list_var

list1 = extendList(10)

list2 = extendList(123,[])

list3 = extendList(‘a’)

print “list1 = %s” % list1

print “list2 = %s” % list2

print “list3 = %s” % list3

以上代码的输出为:

list1 = [10, ‘a’]

list2 = [123]

list3 = [10, ‘a’]

许多人会认为 list1 应该等于 [10] , list3 应该等于 [‘a’]。 list_var 在 extendList 被调用时会被设置成默认值。

但是,实际情况是默认列表list_var仅在函数被定义时创建一次。当调用extendList()且没有指定list_var参数时,使用的是同一个列表。

因此,list1 和 list3 操作的是同一个列表。而 list2操作的是它创建的列表。

extendList()函数可做如下修改,

def extendList(val, list_var=None):

    if list_var is None:

        list_var = []

    list_var.append(val)

    return list_var

list1 = extendList(10)

list2 = extendList(123,[])

list3 = extendList(‘a’)

print “list1 = %s” % list1

print “list2 = %s” % list2

print “list3 = %s” % list3

”’

list1 = [10]

list2 = [123]

list3 = [‘a’]

”’

碰到以上四个代码输出问题是最让人绝望的。

回复

我来回复
  • 暂无回复内容

注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部