当前位置:天才代写 > tutorial > Python教程 > 开拓中常碰着的Python陷阱和留意点

开拓中常碰着的Python陷阱和留意点

2017-11-02 08:00 星期四 所属: Python教程 浏览:525

最近利用Python的进程中碰着了一些坑,譬喻用datetime.datetime.now()这个可变工具作为函数的默认参数,模块轮回依赖等等。

在此记录一下,利便今后查询和增补。

制止可变工具作为默认参数

在利用函数的进程中,常常会涉及默认参数。在Python中,当利用可变工具作为默认参数的时候,就大概发生非预期的功效。

下面看一个例子:

def append_item(a = 1, b = []):
    b.append(a)
    print b
    
append_item(a=1)
append_item(a=3)
append_item(a=5)

功效为:

[1]
[1, 3]
[1, 3, 5]

从功效中可以看到,当后头两次挪用append_item函数的时候,函数参数b并没有被初始化为[],而是保持了前面函数挪用的值。

之所以获得这个功效,是因为在Python中,一个函数参数的默认值,仅仅在该函数界说的时候,被初始化一次。

下面看一个例子证明Python的这个特性:

class Test(object):  
    def __init__(self):  
        print("Init Test")  
          
def arg_init(a, b = Test()):  
    print(a)  
arg_init(1)  
arg_init(3)  
arg_init(5)

功效为:

Init Test
1
3
5

从这个例子的功效就可以看到,Test类仅仅被实例化了一次,也就是说默认参数跟函数挪用次数无关,仅仅在函数界说的时候被初始化一次。

可变默认参数的正确利用

对付可变的默认参数,我们可以利用下面的模式来制止上面的非预期功效:

def append_item(a = 1, b = None):
    if b is None:
        b = []
    b.append(a)
    print b
    
append_item(a=1)
append_item(a=3)
append_item(a=5)

功效为:

[1]
[3]
[5]

Python中的浸染域

Python的浸染域理会顺序为Local、Enclosing、Global、Built-in,也就是说Python表明器会按照这个顺序理会变量。

看一个简朴的例子:

global_var = 0
def outer_func():
    outer_var = 1
    
    def inner_func():
        inner_var = 2
        
        print "global_var is :", global_var
        print "outer_var is :", outer_var
        print "inner_var is :", inner_var
        
    inner_func()
    
outer_func()

功效为:

global_var is : 0
outer_var is : 1
inner_var is : 2

在Python中,关于浸染域有一点需要留意的是,在一个浸染域内里给一个变量赋值的时候,Python会认为这个变量是当前浸染域的当地变量。

对付这一点也是较量容易领略的,对付下面代码var_func中给num变量举办了赋值,所以此处的num就是var_func浸染域的当地变量。

num = 0
def var_func():
    num = 1
    print "num is :", num
    
var_func()

问题一

可是,当我们通过下面的方法利用变量的时候,就会发生问题了:

num = 0
def var_func():
    print "num is :", num
    num = 1
    
var_func()

功效如下:

UnboundLocalError: local variable 'num' referenced before assignment

之所以发生这个错误,就是因为我们在var_func中给num变量举办了赋值,所以Python表明器会认为num是var_func浸染域的当地变量,可是今世码执行到print "num is :", num语句的时候,num照旧未界说。

问题二

上面的错误照旧较量明明的,尚有一种较量隐蔽的错误形式如下:

li = [1, 2, 3]
def foo():
    li.append(4)
    print li
foo()
def bar():
    li +=[5]
    print li
bar()

#p#分页标题#e#

代码的功效为:

[1, 2, 3, 4]
UnboundLocalError: local variable 'li' referenced before assignment

在foo函数中,按照Python的浸染域理会顺序,该函数中利用了全局的li变量;可是在bar函数中,对li变量举办了赋值,所以li会被看成bar浸染域中的变量。

对付bar函数的这个问题,可以通过global要害字。

li = [1, 2, 3]
def foo():
    li.append(4)
    print li
    
foo()
def bar():
    global li
    li +=[5]
    print li
    
bar()

类属性埋没

在Python中,有类属性和实例属性。类属性是属于类自己的,被所有的类实例共享。

类属性可以通过类名会见和修改,也可以通过类实例举办会见和修改。可是,当实例界说了跟类同名的属性后,类属性就被埋没了。

看下面这个例子:

class Student(object):
    books = ["Python", "JavaScript", "CSS"]
    def __init__(self, name, age):
        self.name = name
        self.age = age
    pass
    
wilber = Student("Wilber", 27)
print "%s is %d years old" %(wilber.name, wilber.age)
print Student.books
print wilber.books
wilber.books = ["HTML", "AngularJS"]
print Student.books
print wilber.books
del wilber.books
print Student.books
print wilber.books

代码的功效如下,起初wilber实例可以直接会见类的books属性,可是当实例wilber界说了名称为books的实例属性之后,wilber实例的books属性就“埋没”了类的books属性;当删除了wilber实例的books属性之后,wilber.books就又对应类的books属性了。

Wilber is 27 years old
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
['HTML', 'AngularJS']
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']

当在Python值利用担任的时候,也要留意类属性的埋没。对付一个类,可以通过类的__dict__属性来查察所有的类属性。

当通过类名来会见一个类属性的时候,会首先查找类的__dict__属性,假如没有找到类属性,就会继承查找父类。可是,假如子类界说了跟父类同名的类属性后,子类的类属性就会埋没父类的类属性。

看一个例子:

class A(object):
    count = 1
    
class B(A):
    pass    
    
class C(A):
    pass        
    
print A.count, B.count, C.count      
B.count = 2
print A.count, B.count, C.count      
A.count = 3
print A.count, B.count, C.count     
print B.__dict__
print C.__dict__

功效如下,当类B界说了count这个类属性之后,就会埋没父类的count属性:

1 1 1
1 2 1
3 2 3
{'count': 2, '__module__': '__main__', '__doc__': None}
{'__module__': '__main__', '__doc__': None}

tuple是“可变的”

在Python中,tuple是不行变工具,可是这里的不行变指的是tuple这个容器总的元素不行变(确切的说是元素的id),可是元素的值是可以改变的。

tpl = (1, 2, 3, [4, 5, 6])
print id(tpl)
print id(tpl[3])
tpl[3].extend([7, 8])
print tpl
print id(tpl)
print id(tpl[3])

#p#分页标题#e#

代码功效如下,对付tpl工具,它的每个元素都是不行变的,可是tpl[3]是一个list工具。也就是说,对付这个tpl工具,id(tpl[3])是不行变的,可是tpl[3]确是可变的。

36764576
38639896
(1, 2, 3, [4, 5, 6, 7, 8])
36764576
38639896

Python的深浅拷贝

在对Python工具举办赋值的操纵中,必然要留意工具的深浅拷贝,一不小心就大概踩坑了。

当利用下面的操纵的时候,会发生浅拷贝的结果:

利用切片[:]操纵

利用工场函数(如list/dir/set)

利用copy模块中的copy()函数

利用copy模块内里的浅拷贝函数copy():

import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.copy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

利用copy模块内里的深拷贝函数deepcopy():

import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.deepcopy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

模块轮回依赖

在Python中利用import导入模块的时候,有的时候会发生模块轮回依赖,譬喻下面的例子,module_x模块和module_y模块彼此依赖,运行module_y.py的时候就会发生错误。

# module_x.py
import module_y
    
def inc_count():
    module_y.count += 1
    print module_y.count
    
    
# module_y.py
import module_x
count = 10
def run():
    module_x.inc_count()
    
run()

       

其实,在编码的进程中就该当制止轮回依赖的环境,可能代码重构的进程中消除轮回依赖。

虽然,上面的问题也是可以办理的,常用的办理步伐就是把引用干系搞清楚,让某个模块在真正需要的时候再导入(一般放到函数内里)。

对付上面的例子,就可以把module_x.py修改为如下形式,在函数内部导入module_y:

# module_x.py
def inc_count():
    import module_y
    module_y.count += 1

文章转自:http://www.cnblogs.com/wilber2013/p/5178620.html

 

    关键字:

天才代写-代写联系方式