Programing

Python 2.x 문제 및 지뢰

lottogame 2020. 11. 29. 09:29
반응형

Python 2.x 문제 및 지뢰


내 질문의 목적은 Python으로 지식 기반을 강화하고 결함과 놀라움을 아는 것을 포함하여 더 나은 그림을 얻는 것입니다. 구체적으로 설명하기 위해 CPython 인터프리터에만 관심이 있습니다.

나는 내 PHP 지뢰 질문에서 배운 것과 비슷한 것을 찾고 있는데 , 대답 중 일부는 나에게 잘 알려져 있지만 두 사람은 경계선이 끔찍했습니다.

업데이트 : 분명히 한 두 사람이 이미 Stack Overflow 외부에서 부분적으로 답변 한 질문에 화가났습니다. 일종의 타협으로 http://www.ferg.org/projects/python_gotchas.html URL이 있습니다 .

여기에있는 하나 또는 두 개의 답변은 위에서 언급 한 사이트에 작성된 내용에서 이미 원본입니다.


기본 인수의 표현식은 함수가 호출 될 때가 아니라 정의 될 때 계산됩니다 .

예 : 인수를 현재 시간으로 기본 설정하는 것을 고려하십시오.

>>>import time
>>> def report(when=time.time()):
...     print when
...
>>> report()
1210294387.19
>>> time.sleep(5)
>>> report()
1210294387.19

when인수는 변경되지 않습니다. 함수를 정의 할 때 평가됩니다. 응용 프로그램을 다시 시작할 때까지 변경되지 않습니다.

전략 : 인수를 기본으로 설정 None한 다음 볼 때 유용한 작업을 수행 하면이 문제를 해결하지 않을 것입니다.

>>> def report(when=None):
...     if when is None:
...         when = time.time()
...     print when
...
>>> report()
1210294762.29
>>> time.sleep(5)
>>> report()
1210294772.23

운동 : 이해했는지 확인하기 위해 : 왜 이런 일이 발생합니까?

>>> def spam(eggs=[]):
...     eggs.append("spam")
...     return eggs
...
>>> spam()
['spam']
>>> spam()
['spam', 'spam']
>>> spam()
['spam', 'spam', 'spam']
>>> spam()
['spam', 'spam', 'spam', 'spam']

파이썬에서 클래스 변수가 어떻게 처리되는지 알고 있어야합니다. 다음 클래스 계층을 고려하십시오.

class AAA(object):
    x = 1

class BBB(AAA):
    pass

class CCC(AAA):
    pass

이제 다음 코드의 출력을 확인하십시오.

>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
>>> print AAA.x, BBB.x, CCC.x
3 2 3

놀랐나요? 클래스 변수가 내부적으로 클래스 객체의 사전으로 처리된다는 사실을 기억한다면 그렇지 않을 것입니다. 읽기 작업 의 경우 현재 클래스의 사전에 변수 이름이 없으면 상위 클래스에서 해당 이름을 검색합니다. 따라서 다음 코드를 다시하지만 설명이 있습니다.

# AAA: {'x': 1}, BBB: {}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
# AAA: {'x': 1}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
# AAA: {'x': 3}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
3 2 3

클래스 인스턴스에서 클래스 변수를 처리 할 때도 마찬가지입니다 (이 예제를 위 예제의 연속으로 처리).

>>> a = AAA()
# a: {}, AAA: {'x': 3}
>>> print a.x, AAA.x
3 3
>>> a.x = 4
# a: {'x': 4}, AAA: {'x': 3}
>>> print a.x, AAA.x
4 3

루프와 람다 (또는 모든 클로저) : 변수는 이름 으로 묶여 있습니다.

funcs = []
for x in range(5):
  funcs.append(lambda: x)

[f() for f in funcs]
# output:
# 4 4 4 4 4

해결 방법은 별도의 함수를 만들거나 인수를 이름으로 전달하는 것입니다.

funcs = []
for x in range(5):
  funcs.append(lambda x=x: x)
[f() for f in funcs]
# output:
# 0 1 2 3 4

동적 바인딩은 변수 이름의 오타를 발견하기 매우 어렵게 만듭니다. 사소한 버그를 고치는 데 30 분을 소비하는 것은 쉽습니다.

편집 : 예 ...

for item in some_list:
    ... # lots of code
... # more code
for tiem in some_other_list:
    process(item) # oops!

제가 파이썬으로 경험 한 가장 큰 놀라움 중 하나는 다음과 같습니다.

a = ([42],)
a[0] += [43, 44]

이것은 튜플의 첫 번째 항목을 업데이트 한 후 TypeError를 발생시키는 것을 제외하고는 예상대로 작동합니다! 그래서 a될 것입니다 ([42, 43, 44],)실행중인 후 +=문을하지만, 어쨌든 예외가있을 것입니다. 반면에 이것을 시도하면

a = ([42],)
b = a[0]
b += [43, 44]

오류가 발생하지 않습니다.


try:
    int("z")
except IndexError, ValueError:
    pass

이것이 작동하지 않는 이유는 IndexError가 포착하는 예외 유형이고 ValueError가 예외를 할당하는 변수의 이름이기 때문입니다.

여러 예외를 포착하는 올바른 코드는 다음과 같습니다.

try:
    int("z")
except (IndexError, ValueError):
    pass

숨겨진 언어 기능에 대한 많은 논의가있었습니다 : hidden-features-of-python . 일부 함정이 언급 된 곳 (그리고 좋은 것들도 있습니다).

또한 Python Warts 를 확인하고 싶을 수도 있습니다 .

하지만 나에게는 정수 나눗셈이 있습니다.

>>> 5/2
2

아마도 원했을 것입니다.

>>> 5*1.0/2
2.5

이 (C와 같은) 동작을 정말로 원한다면 다음과 같이 작성해야합니다.

>>> 5//2
2

플로트에서도 작동하므로 (결국 Python 3 으로 이동하면 작동합니다 ) :

>>> 5*1.0//2
2.0

GvR은 정수 나눗셈이 파이썬 역사 에서 어떻게 작동하는지 설명합니다 .


리스트 슬라이싱 으로 인해 많은 슬픔이 생겼습니다. 실제로 다음 동작을 버그로 간주합니다.

목록 정의 x

>>> x = [10, 20, 30, 40, 50]

액세스 인덱스 2 :

>>> x[2]
30

예상대로.

색인 2에서 목록 끝까지 목록을 분할합니다.

>>> x[2:]
[30, 40, 50]

예상대로.

액세스 인덱스 7 :

>>> x[7]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

다시 말하지만, 예상대로.

그러나 목록의 끝까지 색인 7에서 목록을 분할하십시오.

>>> x[7:]
[]

???

해결책은 목록 분할을 사용할 때 많은 테스트를하는 것입니다. 대신 오류가 발생했으면합니다. 디버그하기 훨씬 쉽습니다.


__init__패키지에 .py를 포함하지 않습니다 . 그 사람은 여전히 ​​나를 잡습니다.


내가 처리 한 유일한 문제 / 놀람은 CPython의 GIL입니다. 어떤 이유로 든 CPython의 파이썬 스레드가 동시에 실행될 것으로 예상한다면 ... 음, 그렇지 않습니다. 이것은 파이썬 군중과 심지어 Guido 자신에 의해 꽤 잘 문서화되었습니다.

CPython 스레딩에 대한 길지만 철저한 설명과 내부에서 진행되는 몇 가지 사항과 CPython과의 진정한 동시성이 불가능한 이유. http://jessenoller.com/2009/02/01/python-threads-and-the-global-interpreter-lock/


James Dumay는 나에게 또 다른 Python 문제를 웅변 적으로 상기시켰다 .

Python의 "포함 된 배터리"가 모두 훌륭하지는 않습니다 .

제임스의 구체적인 예였다 HTTP 라이브러리 : httplib, urllib, urllib2, urlparse, mimetools,와 ftplib. 일부 기능은 중복되고 예상 할 수있는 일부 기능 (예 : 리디렉션 처리)이 완전히 없습니다. 솔직히 끔찍합니다.

요즘 HTTP를 통해 무언가를 가져와야 한다면 Yum 프로젝트에서 포크 된 urlgrabber 모듈을 사용합니다 .


부동 소수점은 기본적으로 완전한 정밀도로 인쇄되지 않습니다 (없이 repr).

x = 1.0 / 3
y = 0.333333333333
print x  #: 0.333333333333
print y  #: 0.333333333333
print x == y  #: False

repr 너무 많은 숫자를 인쇄합니다.

print repr(x)  #: 0.33333333333333331
print repr(y)  #: 0.33333333333300003
print x == 0.3333333333333333  #: True

의도하지 않게 oldstyle과 newstyle 클래스를 혼합 하면 신비한 오류가 발생할 수 있습니다.

수퍼 클래스 A와 서브 클래스 B로 구성된 간단한 클래스 계층이 있다고 가정합니다. B가 인스턴스화되면 A의 생성자가 먼저 호출되어야합니다. 아래 코드는이를 올바르게 수행합니다.

class A(object):
    def __init__(self):
        self.a = 1

class B(A):
    def __init__(self):
        super(B, self).__init__()
        self.b = 1

b = B()

그러나 A를 newstyle 클래스로 만들고 다음과 같이 정의하는 것을 잊은 경우 :

class A:
    def __init__(self):
        self.a = 1

이 추적을 얻습니다.

Traceback (most recent call last):
  File "AB.py", line 11, in <module>
    b = B()
  File "AB.py", line 7, in __init__
    super(B, self).__init__()
TypeError: super() argument 1 must be type, not classobj

이 문제와 관련된 다른 두 가지 질문은 489269770134입니다.


def f():
    x += 1

x = 42
f()

UnboundLocalError로컬 이름이 정적으로 감지되기 ​​때문에 결과는 . 다른 예는

def f():
    print x
    x = 43

x = 42
f()

예상대로 지역 변수 값을 변경하기 위해 locals () [ 'x'] = 무엇이든 사용할 수 없습니다.

This works:

>>> x = 1
>>> x
1
>>> locals()['x'] = 2
>>> x
2

BUT:

>>> def test():
...     x = 1
...     print x
...     locals()['x'] = 2
...     print x  # *** prints 1, not 2 ***
...
>>> test()
1
1

This actually burnt me in an answer here on SO, since I had tested it outside a function and got the change I wanted. Afterwards, I found it mentioned and contrasted to the case of globals() in "Dive Into Python." See example 8.12. (Though it does not note that the change via locals() will work at the top level as I show above.)


List repetition with nested lists

This caught me out today and wasted an hour of my time debugging:

>>> x = [[]]*5
>>> x[0].append(0)

# Expect x equals [[0], [], [], [], []]
>>> x
[[0], [0], [0], [0], [0]]   # Oh dear

Explanation: Python list problem


x += [...] is not the same as x = x + [...] when x is a list`

>>> x = y = [1,2,3]
>>> x = x + [4]
>>> x == y
False

>>> x = y = [1,2,3]
>>> x += [4]
>>> x == y
True

One creates a new list while the other modifies in place


Using class variables when you want instance variables. Most of the time this doesn't cause problems, but if it's a mutable value it causes surprises.

class Foo(object):
    x = {}

But:

>>> f1 = Foo()
>>> f2 = Foo()
>>> f1.x['a'] = 'b'
>>> f2.x
{'a': 'b'}

You almost always want instance variables, which require you to assign inside __init__:

class Foo(object):
    def __init__(self):
        self.x = {}

Python 2 has some surprising behaviour with comparisons:

>>> print x
0
>>> print y
1
>>> x < y
False

What's going on? repr() to the rescue:

>>> print "x: %r, y: %r" % (x, y)
x: '0', y: 1

If you assign to a variable inside a function, Python assumes that the variable is defined inside that function:

>>> x = 1
>>> def increase_x():
...     x += 1
... 
>>> increase_x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in increase_x
UnboundLocalError: local variable 'x' referenced before assignment

Use global x (or nonlocal x in Python 3) to declare you want to set a variable defined outside your function.


The values of range(end_val) are not only strictly smaller than end_val, but strictly smaller than int(end_val). For a float argument to range, this might be an unexpected result:

from future.builtins import range
list(range(2.89))
[0, 1]

Due to 'truthiness' this makes sense:

>>>bool(1)
True

but you might not expect it to go the other way:

>>>float(True)
1.0

This can be a gotcha if you're converting strings to numeric and your data has True/False values.


If you create a list of list this way:

arr = [[2]] * 5 
print arr 
[[2], [2], [2], [2], [2]]

Then this creates an array with all elements pointing to the same object ! This might create a real confusion. Consider this:

arr[0][0] = 5

then if you print arr

print arr
[[5], [5], [5], [5], [5]]

The proper way of initializing the array is for example with a list comprehension:

arr = [[2] for _ in range(5)]

arr[0][0] = 5

print arr

[[5], [2], [2], [2], [2]]

참고URL : https://stackoverflow.com/questions/530530/python-2-x-gotchas-and-landmines

반응형