Python踩过的那些坑(1)

1、函数的默认函数一定要是不变对象(immutable)

  • str
  • int
  • float
  • tuple
  • 数值型(number)

看下面一个例子:

def foo(bar=[]):
    bar.append('a')
    return bar
print(foo())#['a']
print(foo())#['a','a']
print(foo())#['a','a','a']

乍一眼一看,每次调用foo(),变量bar都应该重置为[]啊,为什么上一次的结果会进行保留呢?
从Python文档中可以找到这样一句话

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

翻译过来就是:重要警告:默认值只计算一次。当默认值是可变对象(如列表,字典或大多数类的实例)时,这会有所不同。例如,以下函数会累积在后续调用中传递给它的参数。


黄哥给出了答案:
https://zhuanlan.zhihu.com/p/37064591

这个原因是由于默认参数只计算一次,因为list 是可变数据类型,函数每次调用时,L 是同一个对象的引用。

在Stackoverflow上也找到了答案:
https://stackoverflow.com/questions/25204126/python-function-optional-argument-evaluated-once

In Python, functions are objects too, and the defaults are stored with the function object. Defaults are not locals; it is just that when the function is called, the arguments are bound to a default when not given an explicit value.

When Python encounters a def (): statement, it creates a function object for you there and then; this is ‘definition time’; the function is not called but merely created. It is then that defaults are evaluated and stored, in an attribute on the function object.

Then when you call the function, the defaults have already been created and are used when you didn’t provide a more concrete value for the argument. Because the defaults are stored with the function object, you get to see changes to mutable objects between function calls.

The locals are still cleared up of course, but as they are references (all identifiers in Python are), the objects they were bound to are only cleared up if nothing else is referencing them anymore either.

You can take a look a the defaults of any function object:

1
2
3
4
5
6
7
8
9
10
11
12
>>> def foo(bar='spam', eggs=[]):
... eggs.append(bar)
... return eggs
...
>>> foo.__defaults__
('spam', [])
>>> foo()
['spam']
>>> foo.__defaults__
('spam', ['spam'])
>>> foo() is foo.__defaults__[1]
True

The foo() function has a defaults attribute, a tuple of default values to use when no values for the arguments have been passed in. You can see the mutable list change as the function is called, and because the function returns the eggs list, you can also see that it is the exact same object as the second value in that tuple.

那么进行改进:

def foo(bar=None):
    if bar is None:
        bar=[]
    bar.append('a')
print(foo())#['a']
print(foo())#['a']
print(foo())#['a']

最重要的,记住!!!,默认参数用 immutable 类型!

2、Python 惰性求值(Lazy Evaluation)

在编程语言理论中,惰性求值(英语:Lazy Evaluation),又译为惰性计算、懒惰求值,也称为传需求调用(call-by-need),是一个计算机编程中的一个概念,它的目的是要最小化计算机要做的工作。它有两个相关而又有区别的含意,可以表示为“延迟求值”和“最小化求值”,除可以得到性能的提升外,惰性计算的最重要的好处是它可以构造一个无限的数据类型。

惰性求值的相反是及早求值,这是一个大多数编程语言所拥有的普通计算方式。

延迟求值特别用于函数式编程语言中。在使用延迟求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,也就是说,语句如x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到x中,但是先不管实际在x中的是什么,直到通过后面的表达式中到x的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。

以上内容来源于维基百科

li = [lambda :x for x in range(10)]
res = li[0]()
print(res)

#输出:9

但是为什么是9呢。。。真是令人苦恼


黄哥的答案:
https://zhuanlan.zhihu.com/p/37063984

黄哥Python回答: 由于编程语言延迟求值的特性,在使用延迟求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,

当调用li[0]() 函数时,x的值已经是9了,所以输出的是9

那么再来一个,应该可以看出不同了

1.

先定义了一个函数f(),之后在list中进行10次调用

f = lambda x: x*x
[f(x) for x in range(10)]

上面和下面的写法等效

[(lambda x:x*x)(x) for x in range(10)]

out_1、

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

2、

第二个没有调用函数,它创建了10个lambda匿名函数,并把他们放进了一个list中。

[lambda x: x*x for x in range(10)]

out_2:

[<function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
<function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>,
 <function __main__.<listcomp>.<lambda>>]

看下面的代码

a=[lambda x: x*x for x in range(10)]
for aa in a:
    print(aa(5))

输出结果是

25
25
25
25
25
25
25
25
25
25

3、Generator

看一个有趣的

li = [(lambda  :x for x in range(5))]
#print(len(li[0]))
for i  in li[0]:
    print(i())
print('-------------------------')
li = [lambda  :x for x in range(5)]
for i in li:
    print(i())

输出:

0
1
2
3
4
-------------------------
4
4    
4
4
4

就问懵不懵:
上面的那个将 列表生成式的[]改成了()就变成了generator

创建generator的方法

1、像上面所说将列表生成式该成圆括号

2、利用yield来定义

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
for f in fib(6):
    print(f)

今天是520,嗯,该干啥干啥吧,LOL~
继续搬砖,加油加油,挺住挺住

相关链接:

[1]http://python.jobbole.com/86465/

[2]http://www.jb51.net/article/51939.htm

[3]https://blog.csdn.net/ask_man/article/details/39379225

坚持原创分享,您的支持将鼓励我继续创作!