2.python中函数相关的坑

主要是笔者在实际开发中遇到的python函数相关的问题,关系到对python一些概念的深入理解,记下供参考。

1.局部变量作用域问题

python支持闭包,默认闭包中引用的变量读时按照由内而外的顺序(局部->外部->全局->内建)来搜索,但是在写时默认只找当前最近作用域,如果当前作用域没有则默认赋值时新建一个局部变量,如下代码:

def test_local_var():
    var1 = 10
    var2 = 20

    def inner_func():
        print 'inner var1:%s id:%s' % (var1, id(var1))

        var2 = 200
        print 'inner var2:%s id:%s' % (var2, id(var2))

    inner_func()
    print 'outter var1:%s id:%s' % (var1, id(var1))
    print 'outter var2:%s id:%s' % (var2, id(var2))

输出结果如下,这里var1为只读,var2为写,因此内部和外部的var1是同一个变量,内部和外部var2并非同一个变量,在闭包中改变var2的值也就是改变inner_func作用域下的var2的值,对外部var2没有影响。

inner var1:10 id:41183120
inner var2:200 id:41188520
outter var1:10 id:41183120
outter var2:20 id:41182880

那么怎么才能解决这个问题呢,在python3中只需要对内部的var2指明nonlocal参数即可,但是在python2中没有这个关键字,我们常用技巧是先让python去读然后写,其实就是将值存在mutable类型容器中,如下:

def test_local_var2():
    var1 = [10]
    var2 = [20]

    def inner_func():
        print 'inner var1:%s id:%s' % (var1, id(var1))

        var2[0] = 200
        print 'inner var2:%s id:%s' % (var2, id(var2))

    inner_func()
    print 'outter var1:%s id:%s' % (var1, id(var1))
    print 'outter var2:%s id:%s' % (var2, id(var2))

输出结果如下,这里内外层变量取值一致,这里要对var2[0]赋值时必须先找到var2,因此先找到外层作用域中的var2,赋值都是针对它。

inner var1:[10] id:46550216
inner var2:[200] id:46606408
outter var1:[10] id:46550216
outter var2:[200] id:46606408

2.迭代器反复调用问题

在python中迭代器是改进超大列表(大文件/大数据)常用的方法,但是因为迭代器只能用一次,用完后再使用会指向之前的末尾。实际使用中我们经常需要多次使用迭代器,如下:

def generate_iter():
    for i in range(0, 10):
        yield i

def test_iter1():
    l = generate_iter()
    total = sum(l)
    print total

    print(list(l))
    
    for i in l:
        print i

输出如下:

45
[]

这里反复使用了迭代器三次,但是第一次使用完成后迭代器已经指向末尾,所以转换成list为空,遍历输出也为空。如果迭代器内容很少,我们可以转换成列表,在列表上反复调用,关键是使用迭代器的很多场合有超大数据,因此不适合转成列表保存在内存中

可以如下对上述迭代器进行改进,使用容器包装如下:

class RepeatIter():
    def __init__(self):
        pass

    def __iter__(self):
        for i in range(0, 10):
            yield i


def test_iter2():
    l = RepeatIter()
    total = sum(l)
    print total

    print(list(l))

    for i in l:
        print i

输出结果如下,这里容器类实现了__iter__,这样每次使用迭代器时相当于调用iter(l)即调用__iter__生成新的迭代器,因此始终有效

45
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0
1
2
3
4
5
6
7
8
9

演示代码下载链接

扫描二维码关注公众号,回复: 2670540 查看本文章

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

猜你喜欢

转载自blog.csdn.net/wenzhou1219/article/details/81544070