>  기사  >  백엔드 개발  >  개발 중 흔히 접하게 되는 Python 트랩 및 주의사항에 대한 자세한 설명

개발 중 흔히 접하게 되는 Python 트랩 및 주의사항에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-03-16 17:05:391086검색

최근 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의 범위 확인 순서는 로컬, 둘러싸기, 전역, 내장입니다. 즉, 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()

문제 1

그런데, 변수를 아래와 같이 사용하면 문제가 발생합니다.

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 범위의 지역 변수라고 생각하지만 코드가 실행되어 "num is:"를 인쇄하기 때문입니다. num 문 현재로서는 num이 아직 정의되지 않았습니다.


질문 2

위의 오류는 매우 명백하며 다음과 같은 좀 더 미묘한 오류 형식도 있습니다.

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

코드 결과는 다음과 같습니다:

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

foo 함수에서는 Python의 범위 결정 순서에 따라 전역 li 변수가 이 함수에 사용되지만 bar 함수에서는 li 변수에 값이 할당되므로 li는 막대 범위에서는 변수로 처리됩니다.


bar 함수의 이 문제에는 전역 키워드를 사용할 수 있습니다.

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

클래스속성Hide

파이썬에는 클래스 속성과 인스턴스 속성이 있습니다. 클래스 속성은 클래스 자체에 속하며 모든 클래스 인스턴스에서 공유됩니다.

클래스 속성은 클래스 이름을 통해 액세스 및 수정이 가능하며, 클래스 인스턴스를 통해서도 액세스 및 수정이 가능합니다. 그러나 인스턴스가 클래스와 동일한 이름의 속성을 정의하면 클래스 속성이 숨겨집니다.


다음 예를 보세요.

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 인스턴스에서 직접 액세스할 수 있습니다. 하지만 Wilber 인스턴스가 books라는 인스턴스 속성을 정의한 후 Wilber 인스턴스의 books 속성은 Wilber의 books 속성을 삭제할 때 클래스의 books 속성을 "숨깁니다". 예를 들어, Wilber.books는 클래스 속성의 책에 해당합니다.

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

파이썬 값에서 상속을 사용할 때는 클래스 속성이 숨겨지는 것도 주의해야 합니다. 클래스의 경우 클래스의 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])

代码结果如下,对于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


위 내용은 개발 중 흔히 접하게 되는 Python 트랩 및 주의사항에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.