놀라운 조각을 통해 Python을 탐색하고 이해합니다.
번역: 중국어 中文 | 베트남어 Tiếng Viet | 스페인어 스페인어 | 한국어 | 러시아어 Русский | 독일어 독일어 | 번역 추가
기타 모드: 대화형 웹사이트 | 대화형 노트북
아름답게 디자인된 고급 인터프리터 기반 프로그래밍 언어인 Python은 프로그래머의 편의를 위한 많은 기능을 제공합니다. 그러나 때로는 Python 코드 조각의 결과가 첫눈에 명확해 보이지 않을 수도 있습니다.
다음은 직관에 반하는 일부 코드 조각과 Python의 잘 알려지지 않은 기능에 대해 내부적으로 정확히 무슨 일이 일어나고 있는지 설명하려는 재미있는 프로젝트입니다.
아래에 보이는 일부 예제는 진정한 의미에서 WTF가 아닐 수도 있지만, 여러분이 알지 못할 수도 있는 Python의 흥미로운 부분 중 일부를 드러낼 것입니다. 저는 이것이 프로그래밍 언어의 내부를 배울 수 있는 좋은 방법이라고 생각하며 여러분도 흥미로울 것이라고 믿습니다!
숙련된 Python 프로그래머라면 첫 번째 시도에서 대부분의 작업을 올바르게 수행하는 것이 어려울 수 있습니다. 당신은 이미 그들 중 일부를 경험했을 수도 있고, 나는 당신의 달콤한 옛 추억을 되살릴 수도 있습니다! ?
추신: 기존 독자라면 여기에서 새로운 수정 사항에 대해 알아볼 수 있습니다(별표로 표시된 예는 최신 주요 개정판에 추가된 예입니다).
자, 여기 갑니다...
is
운영자is not ...
아니다 is (not ...)
del
운영goto
, 그런데 왜?+=
가 더 빠릅니다.dict
조회 속도 저하 *dict
s *모든 예제는 아래와 같이 구성됩니다.
▶ 화려한 제목
# Set up the code. # Preparation for the magic...출력(Python 버전):
> >> triggering_statement Some unexpected output(선택 사항): 예상치 못한 출력을 설명하는 한 줄입니다.
설명:
- 무슨 일이 일어나고 있는지, 그리고 왜 그런 일이 일어나는지에 대한 간략한 설명입니다.
# Set up code # More examples for further clarification (if necessary)출력(Python 버전):
> >> trigger # some example that makes it easy to unveil the magic # some justified output
참고: 모든 예제는 Python 3.5.2 대화형 인터프리터에서 테스트되었으며 출력 전에 명시적으로 지정하지 않는 한 모든 Python 버전에서 작동해야 합니다.
제 생각에는 이러한 예제를 최대한 활용하는 좋은 방법은 모든 예제를 순차적으로 읽는 것입니다.
어떤 이유에서인지 Python 3.8의 "Walrus" 연산자( :=
)가 꽤 유명해졌습니다. 확인해 봅시다.
1.
# Python version 3.8+
> >> a = "wtf_walrus"
> >> a
'wtf_walrus'
> >> a := "wtf_walrus"
File "<stdin>" , line 1
a := "wtf_walrus"
^
SyntaxError : invalid syntax
>> > ( a := "wtf_walrus" ) # This works though
'wtf_walrus'
> >> a
'wtf_walrus'
2 .
# Python version 3.8+
> >> a = 6 , 9
> >> a
( 6 , 9 )
> >> ( a := 6 , 9 )
( 6 , 9 )
> >> a
6
> >> a , b = 6 , 9 # Typical unpacking
> >> a , b
( 6 , 9 )
>> > ( a , b = 16 , 19 ) # Oops
File "<stdin>" , line 1
( a , b = 16 , 19 )
^
SyntaxError : invalid syntax
>> > ( a , b := 16 , 19 ) # This prints out a weird 3-tuple
( 6 , 16 , 19 )
>> > a # a is still unchanged?
6
>> > b
16
빠른 바다코끼리 오퍼레이터 재교육
Walrus 연산자( :=
)는 Python 3.8에서 도입되었으며, 표현식 내의 변수에 값을 할당하려는 상황에 유용할 수 있습니다.
def some_func ():
# Assume some expensive computation here
# time.sleep(1000)
return 5
# So instead of,
if some_func ():
print ( some_func ()) # Which is bad practice since computation is happening twice
# or
a = some_func ()
if a :
print ( a )
# Now you can concisely write
if a := some_func ():
print ( a )
출력(> 3.8):
5
5
5
이렇게 하면 코드 한 줄이 절약되고 some_func
두 번 호출하는 것이 암묵적으로 방지됩니다.
괄호로 묶이지 않은 "할당 표현식"(바다코끼리 연산자 사용)은 최상위 수준에서 제한되므로 첫 번째 조각의 a := "wtf_walrus"
문에 SyntaxError
발생합니다. a
로 묶으면 예상대로 작동하고 .
평소와 같이 =
연산자가 포함된 표현식을 괄호로 묶는 것은 허용되지 않습니다. 따라서 (a, b = 6, 9)
의 구문 오류입니다.
Walrus 연산자의 구문은 NAME:= expr
형식입니다. 여기서 NAME
은 유효한 식별자이고 expr
은 유효한 표현식입니다. 따라서 반복 가능한 패킹 및 언패킹은 지원되지 않습니다.
(a := 6, 9)
((a := 6), 9)
및 궁극적으로 (a, 9)
( a
'값은 6')와 동일합니다.
> >> ( a := 6 , 9 ) == (( a := 6 ), 9 )
True
> >> x = ( a := 696 , 9 )
> >> x
( 696 , 9 )
> >> x [ 0 ] is a # Both reference same memory location
True
마찬가지로 (a, b := 16, 19)
는 3-튜플에 불과한 (a, (b := 16), 19)
와 동일합니다.
1.
> >> a = "some_string"
> >> id ( a )
140420665652016
> >> id ( "some" + "_" + "string" ) # Notice that both the ids are same.
140420665652016
2.
> >> a = "wtf"
> >> b = "wtf"
> >> a is b
True
> >> a = "wtf!"
> >> b = "wtf!"
> >> a is b
False
3.
> >> a , b = "wtf!" , "wtf!"
> >> a is b # All versions except 3.7.x
True
> >> a = "wtf!" ; b = "wtf!"
> >> a is b # This will print True or False depending on where you're invoking it (python shell / ipython / as a script)
False
# This time in file some_file.py
a = "wtf!"
b = "wtf!"
print ( a is b )
# prints True when the module is invoked!
4.
출력(< Python3.7)
> >> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
> >> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False
말이 되네요, 그렇죠?
'wtf'
인터닝되지만 ''.join(['w', 't', 'f'])
인터닝되지 않습니다).'wtf!'
를 설명합니다. 때문에 억류되지 않았습니다 !
. 이 규칙의 CPython 구현은 여기에서 찾을 수 있습니다. a
와 b
"wtf!"
로 설정된 경우 같은 줄에서 Python 인터프리터는 새 객체를 생성한 다음 동시에 두 번째 변수를 참조합니다. 별도의 줄에서 수행하면 이미 "wtf!"
가 있다는 것을 "알지" 못합니다. 객체로 사용됩니다(위에서 언급한 사실에 따라 "wtf!"
암시적으로 억류되지 않기 때문입니다). 컴파일 타임 최적화입니다. 이 최적화는 CPython 3.7.x 버전에는 적용되지 않습니다(자세한 내용은 이 문제를 확인하세요).a, b = "wtf!", "wtf!"
단일 명령문인 반면 a = "wtf!"; b = "wtf!"
한 줄에 두 개의 명령문이 있습니다. 이는 a = "wtf!"; b = "wtf!"
, 그리고 some_file.py
에서 호출될 때 왜 동일한지 설명합니다.'a'*20
표현식이 'aaaaaaaaaaaaaaaaaaaa'
로 대체됨을 의미합니다. 상수 접기는 길이가 21보다 작은 문자열에 대해서만 발생합니다. (왜? 'a'*10**10
표현식의 결과로 생성된 .pyc
파일의 크기를 상상해 보세요). 동일한 구현 소스는 다음과 같습니다. > >> ( False == False ) in [ False ] # makes sense
False
> >> False == ( False in [ False ]) # makes sense
False
> >> False == False in [ False ] # now what?
True
> >> True is False == False
False
> >> False is False is False
True
> >> 1 > 0 < 1
True
> >> ( 1 > 0 ) < 1
False
> >> 1 > ( 0 < 1 )
False
https://docs.python.org/3/reference/expressions.html#comparisons에 따름
공식적으로 a, b, c, ..., y, z가 표현식이고 op1, op2, ..., opN이 비교 연산자인 경우 a op1 b op2 c ... y opN z는 a op1 b와 동일합니다. b op2 c 및 ... y opN z. 단, 각 표현식은 최대 한 번만 평가됩니다.
위의 예에서는 이러한 동작이 어리석게 보일 수 있지만 a == b == c
및 0 <= x <= 100
과 같은 작업에서는 환상적입니다.
False is False is False
(False is False) and (False is False)
와 동일합니다.True is False == False
(True is False) and (False == False)
와 동일하며 문의 첫 번째 부분( True is False
)이 False
로 평가되므로 전체 표현식은 False
로 평가됩니다.1 > 0 < 1
(1 > 0) and (0 < 1)
과 동일하며 True
로 평가됩니다.(1 > 0) < 1
표현식은 True < 1
과 동일하며 > >> int ( True )
1
> >> True + 1 #not relevant for this example, but just for fun
2
1 < 1
False
로 평가됩니다.is
운영자다음은 인터넷 전체에 존재하는 매우 유명한 예입니다.
1.
> >> a = 256
> >> b = 256
> >> a is b
True
> >> a = 257
> >> b = 257
> >> a is b
False
2.
> >> a = []
> >> b = []
> >> a is b
False
> >> a = tuple ()
> >> b = tuple ()
> >> a is b
True
3. 출력
> >> a , b = 257 , 257
> >> a is b
True
출력(Python 3.7.x 특히)
> >> a , b = 257 , 257
> >> a is b
False
is
와 ==
의 차이점
is
연산자는 두 피연산자가 동일한 객체를 참조하는지 확인합니다(즉, 피연산자의 ID가 일치하는지 확인합니다).==
연산자는 두 피연산자의 값을 비교하여 동일한지 확인합니다.is
참조 평등을 위한 것이고 ==
값 평등을 위한 것입니다. 일을 정리하는 예, > >> class A : pass
> >> A () is A () # These are two empty objects at two different memory locations.
False
256
은 기존 개체이지만 257
그렇지 않습니다.
Python을 시작하면 -5
에서 256
까지의 숫자가 할당됩니다. 이 숫자는 많이 사용되므로 준비해 두는 것이 좋습니다.
https://docs.python.org/3/c-api/long.html에서 인용
현재 구현에서는 -5에서 256 사이의 모든 정수에 대해 정수 객체 배열을 유지합니다. 해당 범위에서 int를 생성하면 기존 객체에 대한 참조만 다시 얻을 수 있습니다. 따라서 1의 값을 변경하는 것이 가능해야 합니다. 이 경우 Python의 동작이 정의되지 않은 것 같습니다. :-)
> >> id ( 256 )
10922528
> >> a = 256
> >> b = 256
> >> id ( a )
10922528
> >> id ( b )
10922528
> >> id ( 257 )
140084850247312
> >> x = 257
> >> y = 257
> >> id ( x )
140084850247440
> >> id ( y )
140084850247344
여기서 인터프리터는 y = 257
실행하는 동안 우리가 이미 값 257,
의 정수를 생성했음을 인식할 만큼 충분히 똑똑하지 않으므로 계속해서 메모리에 다른 객체를 생성합니다.
빈 튜플과 같은 다른 불변 객체에도 유사한 최적화가 적용됩니다. 목록은 변경 가능하므로 [] is []
False
반환하고 () is ()
True
반환합니다. 이것은 두 번째 스니펫을 설명합니다. 세 번째로 넘어가자면,
a
와 b
모두 같은 줄에서 같은 값으로 초기화될 때 같은 객체를 참조합니다.
산출
> >> a , b = 257 , 257
> >> id ( a )
140640774013296
> >> id ( b )
140640774013296
> >> a = 257
> >> b = 257
> >> id ( a )
140640774013392
> >> id ( b )
140640774013488
같은 줄에서 a와 b가 257
로 설정되면 Python 인터프리터는 새 객체를 생성한 다음 동시에 두 번째 변수를 참조합니다. 별도의 줄에서 수행하면 개체로 이미 257
있다는 것을 "알지" 못합니다.
이는 컴파일러 최적화이며 특히 대화형 환경에 적용됩니다. 라이브 인터프리터에 두 줄을 입력하면 별도로 컴파일되므로 별도로 최적화됩니다. .py
파일에서 이 예제를 시도한다면 파일이 모두 한 번에 컴파일되기 때문에 동일한 동작을 볼 수 없습니다. 이 최적화는 정수에만 국한되지 않고 문자열("문자열은 까다로운 예" 확인) 및 부동 소수점과 같은 다른 불변 데이터 유형에도 작동합니다.
> >> a , b = 257.0 , 257.0
> >> a is b
True
Python 3.7에서는 이것이 작동하지 않는 이유는 무엇입니까? 추상적인 이유는 이러한 컴파일러 최적화가 구현에 따라 다르기 때문입니다(즉, 버전, OS 등에 따라 변경될 수 있음). 어떤 구현 변경으로 인해 문제가 발생했는지 아직 파악 중입니다. 이 문제에서 업데이트를 확인할 수 있습니다.
1.
some_dict = {}
some_dict [ 5.5 ] = "JavaScript"
some_dict [ 5.0 ] = "Ruby"
some_dict [ 5 ] = "Python"
산출:
> >> some_dict [ 5.5 ]
"JavaScript"
> >> some_dict [ 5.0 ] # "Python" destroyed the existence of "Ruby"?
"Python"
> >> some_dict [ 5 ]
"Python"
> >> complex_five = 5 + 0j
> >> type ( complex_five )
complex
> >> some_dict [ complex_five ]
"Python"
그렇다면 왜 Python이 여기저기에 있습니까?
Python 사전에서 키의 고유성은 동일성 이 아니라 동일성에 의한 것입니다. 따라서 5
, 5.0
및 5 + 0j
는 서로 다른 유형의 개별 개체이지만 동일하므로 둘 다 동일한 dict
(또는 set
)에 있을 수 없습니다. 그 중 하나를 삽입하자마자 고유하지만 동등한 키를 찾으려고 하면 원래 매핑된 값으로 성공합니다( KeyError
로 실패하지 않고).
> >> 5 == 5.0 == 5 + 0j
True
> >> 5 is not 5.0 is not 5 + 0j
True
> >> some_dict = {}
> >> some_dict [ 5.0 ] = "Ruby"
> >> 5.0 in some_dict
True
> >> ( 5 in some_dict ) and ( 5 + 0j in some_dict )
True
이는 항목을 설정할 때도 적용됩니다. 따라서 some_dict[5] = "Python"
수행하면 Python은 해당 키 5.0 -> "Ruby"
사용하여 기존 항목을 찾아 해당 값을 덮어쓰고 원래 키는 그대로 둡니다.
> >> some_dict
{ 5.0 : 'Ruby' }
> >> some_dict [ 5 ] = "Python"
> >> some_dict
{ 5.0 : 'Python' }
그렇다면 어떻게 키를 5.0
대신 5
로 업데이트할 수 있을까요? 실제로 이 업데이트를 제자리에서 수행할 수는 없지만, 먼저 키( del some_dict[5.0]
)를 삭제한 다음 키로 설정( some_dict[5]
)하여 부동 대신 정수 5
키로 얻을 수 있습니다. 5.0
. 드물지만 필요한 경우도 있습니다.
Python은 5.0
포함된 사전에서 어떻게 5
찾았습니까? Python은 해시 함수를 사용하여 모든 항목을 스캔할 필요 없이 일정한 시간에 이 작업을 수행합니다. Python이 dict에서 키 foo
찾을 때 먼저 hash(foo)
(상수 시간으로 실행됨)를 계산합니다. Python에서는 같다고 비교되는 객체도 동일한 해시 값을 가져야 하므로(여기 문서 참조), 5
, 5.0
및 5 + 0j
는 동일한 해시 값을 갖습니다.
> >> 5 == 5.0 == 5 + 0j
True
> >> hash ( 5 ) == hash ( 5.0 ) == hash ( 5 + 0j )
True
참고: 그 반대가 반드시 참인 것은 아닙니다. 동일한 해시 값을 가진 객체 자체가 동일하지 않을 수 있습니다. (이로 인해 해시 충돌이 발생하고 일반적으로 해싱이 제공하는 상수 시간 성능이 저하됩니다.)
class WTF :
pass
산출:
> >> WTF () == WTF () # two different instances can't be equal
False
> >> WTF () is WTF () # identities are also different
False
> >> hash ( WTF ()) == hash ( WTF ()) # hashes _should_ be different as well
True
> >> id ( WTF ()) == id ( WTF ())
True
id
호출되면 Python은 WTF
클래스 객체를 생성하여 id
함수에 전달했습니다. id
함수는 id
(메모리 위치)를 가져와 객체를 버립니다. 개체가 파괴되었습니다.
이 작업을 연속해서 두 번 수행하면 Python은 이 두 번째 객체에도 동일한 메모리 위치를 할당합니다. (CPython에서) id
메모리 위치를 객체 ID로 사용하므로 두 객체의 ID는 동일합니다.
따라서 개체의 ID는 개체의 수명 동안에만 고유합니다. 객체가 파괴된 후 또는 생성되기 전에 다른 객체가 동일한 ID를 가질 수 있습니다.
그런데 왜 is
연산자가 False
로 평가되었나요? 이 스니펫을 통해 살펴보겠습니다.
class WTF ( object ):
def __init__ ( self ): print ( "I" )
def __del__ ( self ): print ( "D" )
산출:
> >> WTF () is WTF ()
I
I
D
D
False
> >> id ( WTF ()) == id ( WTF ())
I
D
I
D
True
보시다시피, 객체가 파괴되는 순서가 여기서 모든 차이를 만들어냈습니다.
from collections import OrderedDict
dictionary = dict ()
dictionary [ 1 ] = 'a' ; dictionary [ 2 ] = 'b' ;
ordered_dict = OrderedDict ()
ordered_dict [ 1 ] = 'a' ; ordered_dict [ 2 ] = 'b' ;
another_ordered_dict = OrderedDict ()
another_ordered_dict [ 2 ] = 'b' ; another_ordered_dict [ 1 ] = 'a' ;
class DictWithHash ( dict ):
"""
A dict that also implements __hash__ magic.
"""
__hash__ = lambda self : 0
class OrderedDictWithHash ( OrderedDict ):
"""
An OrderedDict that also implements __hash__ magic.
"""
__hash__ = lambda self : 0
산출
> >> dictionary == ordered_dict # If a == b
True
> >> dictionary == another_ordered_dict # and b == c
True
> >> ordered_dict == another_ordered_dict # then why isn't c == a ??
False
# We all know that a set consists of only unique elements,
# let's try making a set of these dictionaries and see what happens...
> >> len ({ dictionary , ordered_dict , another_ordered_dict })
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
TypeError : unhashable type : 'dict'
# Makes sense since dict don't have __hash__ implemented, let's use
# our wrapper classes.
> >> dictionary = DictWithHash ()
> >> dictionary [ 1 ] = 'a' ; dictionary [ 2 ] = 'b' ;
> >> ordered_dict = OrderedDictWithHash ()
> >> ordered_dict [ 1 ] = 'a' ; ordered_dict [ 2 ] = 'b' ;
> >> another_ordered_dict = OrderedDictWithHash ()
> >> another_ordered_dict [ 2 ] = 'b' ; another_ordered_dict [ 1 ] = 'a' ;
> >> len ({ dictionary , ordered_dict , another_ordered_dict })
1
> >> len ({ ordered_dict , another_ordered_dict , dictionary }) # changing the order
2
여기서 무슨 일이 일어나고 있는 걸까요?
dictionary
, ordered_dict
, another_ordered_dict
간에 자동동등이 성립하지 않는 이유는 OrderedDict
클래스에서 __eq__
메소드를 구현하는 방식 때문입니다. 문서에서
OrderedDict 객체 간의 동일성 테스트는 순서를 구분하며
list(od1.items())==list(od2.items())
로 구현됩니다.OrderedDict
객체와 기타 Mapping 객체 간의 동일성 테스트는 일반 사전처럼 순서를 구분하지 않습니다.
동작이 동일한 이유는 일반 사전이 사용되는 모든 곳에서 OrderedDict
객체를 직접 대체할 수 있기 때문입니다.
그렇다면 순서를 변경하면 생성된 set
개체의 길이에 영향을 미치는 이유는 무엇입니까? 대답은 자동적 평등이 부족하다는 것입니다. 세트는 고유한 요소의 "순서가 지정되지 않은" 컬렉션이므로 요소가 삽입되는 순서는 중요하지 않습니다. 하지만 이 경우에는 중요합니다. 조금 분해해보자면,
> >> some_set = set ()
> >> some_set . add ( dictionary ) # these are the mapping objects from the snippets above
> >> ordered_dict in some_set
True
> >> some_set . add ( ordered_dict )
> >> len ( some_set )
1
> >> another_ordered_dict in some_set
True
> >> some_set . add ( another_ordered_dict )
> >> len ( some_set )
1
> >> another_set = set ()
> >> another_set . add ( ordered_dict )
> >> another_ordered_dict in another_set
False
> >> another_set . add ( another_ordered_dict )
> >> len ( another_set )
2
> >> dictionary in another_set
True
> >> another_set . add ( another_ordered_dict )
> >> len ( another_set )
2
따라서 불일치는 another_ordered_dict in another_set
가 False
이기 때문에 발생합니다. 왜냐하면 ordered_dict
another_set
에 이미 존재하고 이전에 관찰한 대로 ordered_dict == another_ordered_dict
가 False
이기 때문입니다.
def some_func ():
try :
return 'from_try'
finally :
return 'from_finally'
def another_func ():
for _ in range ( 3 ):
try :
continue
finally :
print ( "Finally!" )
def one_more_func (): # A gotcha!
try :
for i in range ( 3 ):
try :
1 / i
except ZeroDivisionError :
# Let's throw it here and handle it outside for loop
raise ZeroDivisionError ( "A trivial divide by zero error" )
finally :
print ( "Iteration" , i )
break
except ZeroDivisionError as e :
print ( "Zero division error occurred" , e )
산출:
> >> some_func ()
'from_finally'
> >> another_func ()
Finally !
Finally !
Finally !
>> > 1 / 0
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
ZeroDivisionError : division by zero
> >> one_more_func ()
Iteration 0
return
, break
또는 continue
문이 "try…finally" 문의 try
스위트에서 실행될 때 finally
절도 종료되는 동안 실행됩니다.return
문에 의해 결정됩니다. finally
절은 항상 실행되므로 finally
절에서 실행되는 return
문은 항상 마지막으로 실행되는 문이 됩니다.return
또는 break
문을 실행하는 경우 임시로 저장된 예외가 삭제된다는 것입니다. some_string = "wtf"
some_dict = {}
for i , some_dict [ i ] in enumerate ( some_string ):
i = 10
산출:
> >> some_dict # An indexed dict appears.
{ 0 : 'w' , 1 : 't' , 2 : 'f' }
for
문은 Python 문법에서 다음과 같이 정의됩니다.
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
exprlist
할당 대상입니다. 이는 iterable의 각 항목에 대해 {exprlist} = {next_value}
에 해당하는 항목이 실행됨을 의미합니다. 이를 보여주는 흥미로운 예는 다음과 같습니다.
for i in range ( 4 ):
print ( i )
i = 10
산출:
0
1
2
3
루프가 한 번만 실행될 것으로 예상했나요?
설명:
i = 10
은 for 루프가 Python에서 작동하는 방식으로 인해 루프 반복에 영향을 주지 않습니다. 모든 반복이 시작되기 전에 반복자가 제공하는 다음 항목(이 경우 range(4)
)이 압축 해제되고 대상 목록 변수(이 경우 i
)가 할당됩니다. enumerate(some_string)
함수는 각 반복에서 새로운 값 i
(카운터가 올라감)와 some_string
의 문자를 생성합니다. 그런 다음 사전 some_dict
의 (방금 할당된) i
키를 해당 문자로 설정합니다. 루프 풀기는 다음과 같이 단순화될 수 있습니다.
> >> i , some_dict [ i ] = ( 0 , 'w' )
> >> i , some_dict [ i ] = ( 1 , 't' )
> >> i , some_dict [ i ] = ( 2 , 'f' )
> >> some_dict
1.
array = [ 1 , 8 , 15 ]
# A typical generator expression
gen = ( x for x in array if array . count ( x ) > 0 )
array = [ 2 , 8 , 22 ]
산출:
> >> print ( list ( gen )) # Where did the other values go?
[ 8 ]
2.
array_1 = [ 1 , 2 , 3 , 4 ]
gen_1 = ( x for x in array_1 )
array_1 = [ 1 , 2 , 3 , 4 , 5 ]
array_2 = [ 1 , 2 , 3 , 4 ]
gen_2 = ( x for x in array_2 )
array_2 [:] = [ 1 , 2 , 3 , 4 , 5 ]
산출:
> >> print ( list ( gen_1 ))
[ 1 , 2 , 3 , 4 ]
> >> print ( list ( gen_2 ))
[ 1 , 2 , 3 , 4 , 5 ]
3.
array_3 = [ 1 , 2 , 3 ]
array_4 = [ 10 , 20 , 30 ]
gen = ( i + j for i in array_3 for j in array_4 )
array_3 = [ 4 , 5 , 6 ]
array_4 = [ 400 , 500 , 600 ]
산출:
> >> print ( list ( gen ))
[ 401 , 501 , 601 , 402 , 502 , 602 , 403 , 503 , 603 ]
생성기 표현식에서 in
절은 선언 시 평가되지만 조건 절은 런타임에 평가됩니다.
따라서 런타임 전에 array
[2, 8, 22]
목록에 다시 할당되고 1
, 8
및 15
중에서 8
의 개수만 0
보다 크므로 생성기는 8
만 생성합니다.
두 번째 부분에서 g1
과 g2
의 출력 차이는 변수 array_1
과 array_2
에 값이 다시 할당되는 방식 때문입니다.
첫 번째 경우 array_1
새 객체 [1,2,3,4,5]
에 바인딩되며 in
절은 선언 시 평가되므로 여전히 이전 객체 [1,2,3,4]
를 참조합니다. (파괴되지 않음)
두 번째 경우에는 array_2
에 대한 슬라이스 할당이 동일한 이전 객체 [1,2,3,4]
를 [1,2,3,4,5]
로 업데이트합니다. 따라서 g2
와 array_2
여전히 동일한 객체에 대한 참조를 가지고 있습니다(현재 [1,2,3,4,5]
로 업데이트됨).
좋아, 지금까지 논의한 논리에 따르면 세 번째 조각의 list(gen)
값은 [11, 21, 31, 12, 22, 32, 13, 23, 33]
이 아니어야 합니까? ( array_3
과 array_4
array_1
과 똑같이 동작하기 때문입니다). array_4
값만 (오직) 업데이트된 이유는 PEP-289에 설명되어 있습니다.
가장 바깥쪽 for-expression만 즉시 평가되고 다른 표현식은 생성기가 실행될 때까지 연기됩니다.
is not ...
아니다 is (not ...)
> >> 'something' is not None
True
> >> 'something' is ( not None )
False
is not
은 단일 이항 연산자이며 is
사용하는 것과 동작이 다르며 분리되지 not
.False
로 평가되지 is not
그렇지 않으면 True
로 평가됩니다.None
값이 False
이므로 (not None)
은 True
로 평가되므로 표현식은 'something' is True
됩니다. # Let's initialize a row
row = [ "" ] * 3 #row i['', '', '']
# Let's make a board
board = [ row ] * 3
산출:
> >> board
[[ '' , '' , '' ], [ '' , '' , '' ], [ '' , '' , '' ]]
> >> board [ 0 ]
[ '' , '' , '' ]
> >> board [ 0 ][ 0 ]
''
> >> board [ 0 ][ 0 ] = "X"
> >> board
[[ 'X' , '' , '' ], [ 'X' , '' , '' ], [ 'X' , '' , '' ]]
우리는 세 개의 "X"
를 할당하지 않았습니다. 그렇죠?
row
변수를 초기화할 때 이 시각화는 메모리에서 어떤 일이 일어나는지 설명합니다.
그리고 row
곱하여 board
초기화되면 메모리 내부에서 이런 일이 발생합니다(각 요소 board[0]
, board[1]
및 board[2]
는 row
가 참조하는 동일한 목록에 대한 참조입니다).
여기서는 row
변수를 사용하여 board
생성하지 않음으로써 이 시나리오를 피할 수 있습니다. (이번 호에서 질문함)
> >> board = [[ '' ] * 3 for _ in range ( 3 )]
> >> board [ 0 ][ 0 ] = "X"
> >> board
[[ 'X' , '' , '' ], [ '' , '' , '' ], [ '' , '' , '' ]]
funcs = []
results = []
for x in range ( 7 ):
def some_func ():
return x
funcs . append ( some_func )
results . append ( some_func ()) # note the function call here
funcs_results = [ func () for func in funcs ]
출력(Python 버전):
> >> results
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
> >> funcs_results
[ 6 , 6 , 6 , 6 , 6 , 6 , 6 ]
x
값은 some_func
funcs
에 추가하기 전에는 반복할 때마다 달랐지만, 루프가 완료된 후 평가할 때 모든 함수는 6을 반환합니다.
> >> powers_of_x = [ lambda x : x ** i for i in range ( 10 )]
> >> [ f ( 2 ) for f in powers_of_x ]
[ 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 ]
x
값을 사용하는 대신 주변 컨텍스트에서 x
찾습니다. 따라서 모든 함수는 계산을 위해 변수에 할당된 최신 값을 사용합니다. 다음을 통해 주변 컨텍스트(즉, 지역 변수가 아님 )의 x
를 사용하고 있음을 확인할 수 있습니다. > >> import inspect
> >> inspect . getclosurevars ( funcs [ 0 ])
ClosureVars ( nonlocals = {}, globals = { 'x' : 6 }, builtins = {}, unbound = set ())
x
전역 값이므로 x
업데이트하여 funcs
조회하고 반환하는 값을 변경할 수 있습니다.
> >> x = 42
> >> [ func () for func in funcs ]
[ 42 , 42 , 42 , 42 , 42 , 42 , 42 ]
x
값을 저장하는 로컬 변수를 생성합니다. funcs = []
for x in range ( 7 ):
def some_func ( x = x ):
return x
funcs . append ( some_func )
산출:
> >> funcs_results = [ func () for func in funcs ]
> >> funcs_results
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
더 이상 전역 범위에서 x
를 사용하지 않습니다.
> >> inspect . getclosurevars ( funcs [ 0 ])
ClosureVars ( nonlocals = {}, globals = {}, builtins = {}, unbound = set ())
1.
> >> isinstance ( 3 , int )
True
> >> isinstance ( type , object )
True
> >> isinstance ( object , type )
True
그렇다면 "궁극적인" 기본 클래스는 무엇입니까? 그런데 혼란스러운 것이 더 많습니다.
2.
> >> class A : pass
> >> isinstance ( A , A )
False
> >> isinstance ( type , type )
True
> >> isinstance ( object , object )
True
3.
> >> issubclass ( int , object )
True
> >> issubclass ( type , object )
True
> >> issubclass ( object , type )
False
type
Python의 메타클래스입니다.object
입니다.type
은 object
클래스의 메타클래스이며, 모든 클래스( type
포함)는 object
에서 직접 또는 간접적으로 상속되었습니다.object
와 type
사이에는 실제 기본 클래스가 없습니다. 위 코드 조각의 혼란은 Python 클래스 측면에서 이러한 관계( issubclass
및 isinstance
)를 생각하고 있기 때문에 발생합니다. object
와 type
사이의 관계는 순수 파이썬에서는 재현될 수 없습니다. 더 정확하게 말하면 다음 관계는 순수 Python에서는 재현될 수 없습니다.object
와 type
(둘 다 서로의 인스턴스이자 자체 인스턴스임) 사이의 이러한 관계는 구현 수준의 "속임수"로 인해 Python에 존재합니다.산출:
> >> from collections . abc import Hashable
> >> issubclass ( list , object )
True
> >> issubclass ( object , Hashable )
True
> >> issubclass ( list , Hashable )
False
하위 클래스 관계는 전이적일 것으로 예상되었습니다. 그렇죠? (즉, A
가 B
의 하위 클래스이고 B
가 C
의 하위 클래스인 경우 A
는 C
의 하위 클래스 여야 합니다 .)
__subclasscheck__
정의할 수 있습니다.issubclass(cls, Hashable)
가 호출되면 cls
또는 상속받은 모든 항목에서 False가 아닌 " __hash__
" 메서드를 찾습니다.object
해시 가능하지만 list
해시 가능하지 않으므로 전이성 관계가 깨집니다. class SomeClass :
def method ( self ):
pass
@ classmethod
def classm ( cls ):
pass
@ staticmethod
def staticm ():
pass
산출:
> >> print ( SomeClass . method is SomeClass . method )
True
> >> print ( SomeClass . classm is SomeClass . classm )
False
> >> print ( SomeClass . classm == SomeClass . classm )
True
> >> print ( SomeClass . staticm is SomeClass . staticm )
True
classm
두 번 액세스하면 동일한 객체를 얻지만 동일한 객체는 얻지 못합니까? SomeClass
인스턴스에서 무슨 일이 일어나는지 살펴보겠습니다.
o1 = SomeClass ()
o2 = SomeClass ()
산출:
> >> print ( o1 . method == o2 . method )
False
> >> print ( o1 . method == o1 . method )
True
> >> print ( o1 . method is o1 . method )
False
> >> print ( o1 . classm is o1 . classm )
False
> >> print ( o1 . classm == o1 . classm == o2 . classm == SomeClass . classm )
True
> >> print ( o1 . staticm is o1 . staticm is o2 . staticm is SomeClass . staticm )
True
classm
또는 method
두 번 액세스하면 SomeClass
의 동일한 인스턴스에 대해 동일하지만 동일 하지 않은 객체가 생성됩니다.
self
첫 번째 인수로 얻는 방법입니다). > >> o1 . method
< bound method SomeClass . method of < __main__ . SomeClass object at ... >>
o1.method is o1.method
결코 진실이 아닙니다. 그러나 클래스 속성(인스턴스가 아닌)으로 함수에 액세스하면 메서드가 생성되지 않습니다. 따라서 SomeClass.method is SomeClass.method
진실입니다. > >> SomeClass . method
< function SomeClass . method at ... >
classmethod
함수를 클래스 메서드로 변환합니다. 클래스 메소드는 액세스 시 객체 자체 대신 객체의 클래스 (유형)를 바인딩하는 메소드 객체를 생성하는 설명자입니다. > >> o1 . classm
< bound method SomeClass . classm of < class '__main__.SomeClass' >>
classmethod
s는 클래스 속성으로 액세스할 때에도 메서드를 생성합니다(이 경우 클래스의 유형이 아니라 클래스를 바인딩합니다). 따라서 SomeClass.classm is SomeClass.classm
거짓입니다. > >> SomeClass . classm
< bound method SomeClass . classm of < class '__main__.SomeClass' >>
o1.method == o1.method
메모리에 있는 동일한 객체는 아니지만 진실입니다.staticmethod
함수를 있는 그대로 반환하는 "no-op" 설명자로 함수를 변환합니다. 메소드 객체는 생성되지 않으므로 is
와의 비교는 진실입니다. > >> o1 . staticm
< function SomeClass . staticm at ... >
> >> SomeClass . staticm
< function SomeClass . staticm at ... >
self
영향을 받는 성능을 나쁘게 삽입하기 위해 매번 인수를 수정해야 합니다. CPython 3.7에서는 임시 메서드 객체를 생성하지 않고 메서드 호출을 처리하는 새로운 opcode를 도입하여 이 문제를 해결했습니다. 이는 액세스된 함수가 실제로 호출될 때만 사용되므로 여기의 스니펫은 영향을 받지 않으며 여전히 메소드를 생성합니다. :) > >> all ([ True , True , True ])
True
> >> all ([ True , True , False ])
False
> >> all ([])
True
> >> all ([[]])
False
> >> all ([[[]]])
True
True-False 변경이 발생하는 이유는 무엇입니까?
all
기능의 구현은 다음과 같습니다.
def all ( iterable ):
for element in iterable :
if not element :
return False
return True
all([])
iterable이 비어 있으므로 True
반환합니다.
all([[]])
전달된 배열에 하나의 요소 []
가 있고 Python에서는 빈 목록이 거짓이므로 False
를 반환합니다.
all([[[]]])
및 더 높은 재귀 변형은 항상 True
입니다. 이는 전달된 배열의 단일 요소( [[...]]
)가 더 이상 비어 있지 않고 값이 포함된 목록이 진실이기 때문입니다.
출력(< 3.6):
> >> def f ( x , y ,):
... print ( x , y )
...
> >> def g ( x = 4 , y = 5 ,):
... print ( x , y )
...
> >> def h ( x , ** kwargs ,):
File "<stdin>" , line 1
def h ( x , ** kwargs ,):
^
SyntaxError : invalid syntax
>> > def h ( * args ,):
File "<stdin>" , line 1
def h ( * args ,):
^
SyntaxError : invalid syntax
산출:
> >> print ( " " " )
"
>> > print ( r""" )
"
>> > print ( r" " )
File "<stdin>" , line 1
print ( r" " )
^
SyntaxError : EOL while scanning string literal
>> > r''' == " \ '"
True
> >> "wt " f"
'wt"f'
r
로 표시됨)에서 백슬래시는 다음 문자를 이스케이프하는 동작과 함께 그대로 전달됩니다. > >> r'wt"f' == 'wt \ "f'
True
> >> print ( repr ( r'wt"f' ))
'wt \ "f'
> >> print ( " n " )
> >> print ( r"\n" )
' \ n'
print(r"")
) 백슬래시는 후행 따옴표를 이스케이프 처리하여 파서에서 종료 따옴표(따라서 SyntaxError
) 없이 남겨졌습니다. 이것이 바로 원시 문자열 끝에서 백슬래시가 작동하지 않는 이유입니다. x = True
y = False
산출:
> >> not x == y
True
> >> x == not y
File "<input>" , line 1
x == not y
^
SyntaxError : invalid syntax
==
연산자는 Python에서 not
연산자보다 우선 순위가 높습니다.not x == y
not (x == y)
와 동일하며 이는 최종적으로 True
로 평가되는 not (True == False)
와 동일합니다.x == not y
첫눈에 예상했던 (x == not) y
및 not x == (not y)
와 동일하다고 생각할 수 있기 때문에 SyntaxError
발생시킵니다.not
토큰이 not in
연산자의 일부일 것으로 예상했지만( ==
연산자와 not in
연산자의 우선순위가 동일하기 때문에) not
토큰 다음에 오는 in
토큰을 찾을 수 없으면 SyntaxError
발생합니다.산출:
> >> print ( 'wtfpython' '' )
wtfpython
> >> print ( "wtfpython" "" )
wtfpython
> >> # The following statements raise `SyntaxError`
>> > # print('''wtfpython')
>> > # print("""wtfpython")
File "<input>" , line 3
print ("""wtfpython")
^
SyntaxError: EOF while scanning triple-quoted string literal
>>> print("wtf" "python")
wtfpython
>>> print("wtf" "") # or "wtf"""
wtf
'''
및 """
도 Python의 문자열 구분 기호로, Python 해석기가 현재 발견된 삼중 따옴표 문자열 리터럴을 스캔하는 동안 종료 삼중 따옴표를 구분 기호로 예상했기 때문에 SyntaxError를 발생시킵니다.1.
# A simple example to count the number of booleans and
# integers in an iterable of mixed data types.
mixed_list = [ False , 1.0 , "some_string" , 3 , True , [], False ]
integers_found_so_far = 0
booleans_found_so_far = 0
for item in mixed_list :
if isinstance ( item , int ):
integers_found_so_far += 1
elif isinstance ( item , bool ):
booleans_found_so_far += 1
산출:
> >> integers_found_so_far
4
> >> booleans_found_so_far
0
2.
> >> some_bool = True
> >> "wtf" * some_bool
'wtf'
> >> some_bool = False
> >> "wtf" * some_bool
''
3.
def tell_truth ():
True = False
if True == False :
print ( "I have lost faith in truth!" )
출력(< 3.x):
> >> tell_truth ()
I have lost faith in truth !
bool
Python에서 int
의 하위 클래스입니다.
> >> issubclass ( bool , int )
True
> >> issubclass ( int , bool )
False
따라서 True
와 False
int
의 인스턴스입니다.
> >> isinstance ( True , int )
True
> >> isinstance ( False , int )
True
True
의 정수 값은 1
이고 False
의 정수 값은 0
입니다.
> >> int ( True )
1
> >> int ( False )
0
그 이유에 대해서는 이 StackOverflow 답변을 참조하세요.
처음에 Python에는 bool
유형이 없었습니다(사람들은 false에 0을 사용하고 true에 1과 같이 0이 아닌 값을 사용했습니다). 2.x 버전에는 True
, False
및 bool
유형이 추가되었지만 이전 버전과의 호환성을 위해 True
및 False
상수로 만들 수 없었습니다. 그것들은 단지 내장된 변수였고 재할당이 가능했습니다.
Python 3은 이전 버전과 호환되지 않았으며 문제가 마침내 해결되었으므로 마지막 조각은 Python 3.x에서 작동하지 않습니다!
1.
class A :
x = 1
class B ( A ):
pass
class C ( A ):
pass
산출:
> >> A . x , B . x , C . x
( 1 , 1 , 1 )
>> > B . x = 2
>> > A . x , B . x , C . x
( 1 , 2 , 1 )
>> > A . x = 3
>> > A . x , B . x , C . x # C.x changed, but B.x didn't
( 3 , 2 , 3 )
>> > a = A ()
>> > a . x , A . x
( 3 , 3 )
>> > a . x + = 1
>> > a . x , A . x
( 4 , 3 )
2.
class SomeClass :
some_var = 15
some_list = [ 5 ]
another_list = [ 5 ]
def __init__ ( self , x ):
self . some_var = x + 1
self . some_list = self . some_list + [ x ]
self . another_list += [ x ]
산출:
> >> some_obj = SomeClass ( 420 )
> >> some_obj . some_list
[ 5 , 420 ]
> >> some_obj . another_list
[ 5 , 420 ]
> >> another_obj = SomeClass ( 111 )
> >> another_obj . some_list
[ 5 , 111 ]
> >> another_obj . another_list
[ 5 , 420 , 111 ]
> >> another_obj . another_list is SomeClass . another_list
True
> >> another_obj . another_list is some_obj . another_list
True
+=
연산자는 새 객체를 생성하지 않고 변경 가능한 객체를 내부에서 수정합니다. 따라서 한 인스턴스의 속성을 변경하면 다른 인스턴스와 클래스 속성에도 영향을 미칩니다. some_iterable = ( 'a' , 'b' )
def some_func ( val ):
return "something"
출력(<= 3.7.x):
> >> [ x for x in some_iterable ]
[ 'a' , 'b' ]
> >> [( yield x ) for x in some_iterable ]
< generator object < listcomp > at 0x7f70b0a4ad58 >
> >> list ([( yield x ) for x in some_iterable ])
[ 'a' , 'b' ]
> >> list (( yield x ) for x in some_iterable )
[ 'a' , None , 'b' , None ]
> >> list ( some_func (( yield x )) for x in some_iterable )
[ 'a' , 'something' , 'b' , 'something' ]
yield
처리할 때 발생하는 버그입니다.yield
허용하지 않으며 SyntaxError
발생합니다.1.
def some_func ( x ):
if x == 3 :
return [ "wtf" ]
else :
yield from range ( x )
출력(> 3.3):
> >> list ( some_func ( 3 ))
[]
"wtf"
는 어디로 갔습니까? yield from
의 특별한 효과 때문입니까? 그것을 검증해보자.
2.
def some_func ( x ):
if x == 3 :
return [ "wtf" ]
else :
for i in range ( x ):
yield i
산출:
> >> list ( some_func ( 3 ))
[]
같은 결과, 이것도 작동하지 않았습니다.
return
문을 사용할 수 있게 되었습니다(PEP380 참조). 공식 문서에서는 다음과 같이 말합니다."... 생성기에서
return expr
생성기 종료 시StopIteration(expr)
발생합니다."
some_func(3)
의 경우 return
문으로 인해 처음에 StopIteration
이 발생합니다. StopIteration
예외는 list(...)
래퍼와 for
루프 내에서 자동으로 포착됩니다. 따라서 위의 두 조각으로 인해 빈 목록이 생성됩니다.
생성기 some_func
에서 ["wtf"]
얻으려면 StopIteration
예외를 포착해야 합니다.
try :
next ( some_func ( 3 ))
except StopIteration as e :
some_string = e . value
> >> some_string
[ "wtf" ]
1.
a = float ( 'inf' )
b = float ( 'nan' )
c = float ( '-iNf' ) # These strings are case-insensitive
d = float ( 'nan' )
산출:
> >> a
inf
> >> b
nan
> >> c
- inf
> >> float ( 'some_other_string' )
ValueError : could not convert string to float : some_other_string
> >> a == - c # inf==inf
True
> >> None == None # None == None
True
> >> b == d # but nan!=nan
False
> >> 50 / a
0.0
> >> a / a
nan
> >> 23 + b
nan
2.
> >> x = float ( 'nan' )
> >> y = x / x
> >> y is y # identity holds
True
> >> y == y # equality fails of y
False
> >> [ y ] == [ y ] # but the equality succeeds for the list containing y
True
'inf'
및 'nan'
은 특수 문자열(대소문자 구분 없음)로, 명시 float
유형으로 변환될 때 각각 수학적 "무한대"와 "숫자가 아님"을 나타내는 데 사용됩니다.
IEEE 표준 NaN != NaN
에 따르면 이 규칙을 준수하면 Python에서 컬렉션 요소의 재귀성 가정이 깨집니다. 즉, x
list
와 같은 컬렉션의 일부인 경우 비교와 같은 구현은 x == x
라는 가정을 기반으로 합니다. 이러한 가정으로 인해 두 요소를 비교하면서 항등을 먼저 비교하고(빠르기 때문에) 항등이 일치하지 않는 경우에만 값을 비교합니다. 다음 스니펫을 사용하면 상황이 더 명확해집니다.
> >> x = float ( 'nan' )
> >> x == x , [ x ] == [ x ]
( False , True )
> >> y = float ( 'nan' )
> >> y == y , [ y ] == [ y ]
( False , True )
> >> x == y , [ x ] == [ y ]
( False , False )
x
와 y
의 동일성이 다르기 때문에 값도 서로 다른 것으로 간주됩니다. 따라서 이번에는 비교 결과가 False
반환합니다.
흥미로운 읽기: 성찰성과 문명의 다른 기둥
Python에서 참조가 작동하는 방식을 알고 있다면 이는 사소한 것처럼 보일 수 있습니다.
some_tuple = ( "A" , "tuple" , "with" , "values" )
another_tuple = ([ 1 , 2 ], [ 3 , 4 ], [ 5 , 6 ])
산출:
> >> some_tuple [ 2 ] = "change this"
TypeError : 'tuple' object does not support item assignment
> >> another_tuple [ 2 ]. append ( 1000 ) #This throws no error
> >> another_tuple
([ 1 , 2 ], [ 3 , 4 ], [ 5 , 6 , 1000 ])
> >> another_tuple [ 2 ] += [ 99 , 999 ]
TypeError : 'tuple' object does not support item assignment
> >> another_tuple
([ 1 , 2 ], [ 3 , 4 ], [ 5 , 6 , 1000 , 99 , 999 ])
그러나 나는 튜플이 불변이라고 생각했습니다 ...
https://docs.python.org/3/reference/datamodel.html에서 인용
불변 시퀀스 불변 시퀀스 유형의 객체는 일단 생성되면 변경할 수 없습니다. (객체가 다른 객체에 대한 참조를 포함하는 경우 이러한 다른 객체는 변경 가능하고 수정될 수 있습니다. 그러나 불변 객체가 직접 참조하는 객체 컬렉션은 변경할 수 없습니다.)
+=
연산자는 목록을 그 자리에서 변경합니다. 항목 할당이 작동하지 않지만 예외가 발생하면 항목이 이미 변경되었습니다.
공식 Python FAQ에도 설명이 있습니다.
e = 7
try :
raise Exception ()
except Exception as e :
pass
출력(파이썬 2.x):
> >> print ( e )
# prints nothing
출력(파이썬 3.x):
> >> print ( e )
NameError : name 'e' is not defined
출처: https://docs.python.org/3/reference/compound_stmts.html#just
as
대상을 사용하여 예외를 할당한 경우에는 except
절 끝에서 지워집니다. 이것은 마치
except E as N :
foo
로 번역되었습니다
except E as N :
try :
foo
finally :
del N
이는 Except 절 뒤에서 참조할 수 있도록 예외를 다른 이름에 할당해야 함을 의미합니다. 예외는 첨부된 트레이스백을 통해 스택 프레임과 참조 순환을 형성하고 다음 가비지 수집이 발생할 때까지 해당 프레임의 모든 로컬을 활성 상태로 유지하기 때문에 지워집니다.
Python에서는 절의 범위가 지정되지 않습니다. 예제의 모든 내용은 동일한 범위에 존재하며, except
절의 실행으로 인해 변수 e
제거되었습니다. 별도의 내부 스코프가있는 함수의 경우도 마찬가지입니다. 아래 예는 다음을 보여줍니다.
def f ( x ):
del ( x )
print ( x )
x = 5
y = [ 5 , 4 , 3 ]
산출:
> >> f ( x )
UnboundLocalError : local variable 'x' referenced before assignment
>> > f ( y )
UnboundLocalError : local variable 'x' referenced before assignment
>> > x
5
> >> y
[ 5 , 4 , 3 ]
Python 2.X에서는 변수 이름 e
Exception()
인스턴스에 할당되므로 인쇄하려고하면 아무것도 인쇄하지 않습니다.
출력 (Python 2.x) :
> >> e
Exception ()
> >> print e
# Nothing is printed!
class SomeClass ( str ):
pass
some_dict = { 's' : 42 }
산출:
> >> type ( list ( some_dict . keys ())[ 0 ])
str
> >> s = SomeClass ( 's' )
> >> some_dict [ s ] = 40
> >> some_dict # expected: Two different keys-value pairs
{ 's' : 40 }
> >> type ( list ( some_dict . keys ())[ 0 ])
str
SomeClass
str
클래스의 __hash__
메소드를 상속하기 때문에 s
와 문자열 "s"
동일한 값으로 해시합니다.
SomeClass
str
클래스에서 __eq__
메소드를 상속하기 때문에 SomeClass("s") == "s"
True
로 평가됩니다.
두 객체 모두 동일한 값으로 해시되고 동일하기 때문에 사전에서 동일한 키로 표시됩니다.
원하는 동작을 위해, 우리는 SomeClass
의 __eq__
방법을 재정의 할 수 있습니다.
class SomeClass ( str ):
def __eq__ ( self , other ):
return (
type ( self ) is SomeClass
and type ( other ) is SomeClass
and super (). __eq__ ( other )
)
# When we define a custom __eq__, Python stops automatically inheriting the
# __hash__ method, so we need to define it as well
__hash__ = str . __hash__
some_dict = { 's' : 42 }
산출:
> >> s = SomeClass ( 's' )
> >> some_dict [ s ] = 40
> >> some_dict
{ 's' : 40 , 's' : 42 }
> >> keys = list ( some_dict . keys ())
> >> type ( keys [ 0 ]), type ( keys [ 1 ])
( __main__ . SomeClass , str )
a , b = a [ b ] = {}, 5
산출:
> >> a
{ 5 : ({...}, 5 )}
(target_list "=")+ (expression_list | yield_expression)
과제 명령문은 표현 목록을 평가하고 (이것은 단일 표현식 또는 쉼표로 구분 된 목록 일 수 있고, 후자는 튜플을 산출 할 수 있음을 기억하고) 단일 결과 객체를 각각의 대상 목록에 왼쪽에서 오른쪽으로 할당합니다.
+
in (target_list "=")+
하나 이상의 대상 목록이있을 수 있음을 의미합니다. 이 경우 대상 목록은 a, b
및 a[b]
입니다 (표현 목록은 정확히 하나이며,이 경우 {}, 5
).
표현 목록을 평가 한 후 값은 왼쪽에서 오른쪽으로 대상 목록으로 포장되지 않습니다. 따라서, 우리의 경우, 먼저 {}, 5
튜플은 a, b
로 포장되지 않으며 이제 a = {}
및 b = 5
있습니다.
a
이제 {}
에 할당되어 있으며, 이는 돌연변이 가능한 객체입니다.
두 번째 대상 목록은 a[b]
입니다 (A와 B가 모두 명령문에서 a
와 b
모두 정의되지 않았기 때문에 오류가 발생할 것으로 예상 할 수 있습니다. 그러나 우리는 방금 a
에 {}
와 b
~ 5
)를 할당했습니다.
이제 사전에서 키 5
튜플 ({}, 5)
는 원형 참조를 생성합니다 (출력의 {...}
는 a
이미 참조하는 것과 동일한 객체를 나타냅니다). 원형 기준의 또 다른 단순한 예는 일 수 있습니다
> >> some_list = some_list [ 0 ] = [ 0 ]
> >> some_list
[[...]]
> >> some_list [ 0 ]
[[...]]
> >> some_list is some_list [ 0 ]
True
> >> some_list [ 0 ][ 0 ][ 0 ][ 0 ][ 0 ][ 0 ] == some_list
True
이 예제에서도 비슷합니다 ( a[b][0]
은 a
와 동일한 대상입니다)
요약하면 예제를 다음으로 나눌 수 있습니다.
a , b = {}, 5
a [ b ] = a , b
그리고 순환 기준은 a[b][0]
a
> >> a [ b ][ 0 ] is a
True
> >> # Python 3.10.6
>> > int ( "2" * 5432 )
> >> # Python 3.10.8
>> > int ( "2" * 5432 )
산출:
> >> # Python 3.10.6
222222222222222222222222222222222222222222222222222222222222222. ..
>> > # Python 3.10.8
Traceback ( most recent call last ):
...
ValueError : Exceeds the limit ( 4300 ) for integer string conversion :
value has 5432 digits ; use sys . set_int_max_str_digits ()
to increase the limit .
int()
에 대한이 호출은 Python 3.10.6에서 잘 작동하며 Python 3.10.8에서 ValueError를 올립니다. 파이썬은 여전히 큰 정수와 함께 작동 할 수 있습니다. 정수와 문자열 사이를 변환 할 때만 오류가 발생합니다.
다행히도 작업이 초과 될 것으로 예상 할 때 허용되는 숫자 수에 대한 한계를 늘릴 수 있습니다. 이렇게하려면 다음 중 하나를 사용할 수 있습니다.
코드 가이 값을 초과 할 것으로 예상되는 경우 기본 제한 변경에 대한 자세한 내용은 문서를 확인하십시오.
x = { 0 : None }
for i in x :
del x [ i ]
x [ i + 1 ] = None
print ( i )
출력 (Python 2.7- Python 3.5) :
0
1
2
3
4
5
6
7
예, 정확히 8 번 실행되고 멈 춥니 다.
RuntimeError: dictionary keys changed during iteration
.del
조작 class SomeClass :
def __del__ ( self ):
print ( "Deleted!" )
출력 : 1.
> >> x = SomeClass ()
> >> y = x
> >> del x # this should print "Deleted!"
> >> del y
Deleted !
PHEW, 마침내 삭제. x
삭제하려는 첫 번째 시도에서 __del__
호출되지 않은 것을 저장 한 것을 추측했을 것입니다. 예제에 더 많은 비틀림을 추가합시다.
2.
> >> x = SomeClass ()
> >> y = x
> >> del x
> >> y # check if y exists
< __main__ . SomeClass instance at 0x7f98a1a67fc8 >
> >> del y # Like previously, this should print "Deleted!"
> >> globals () # oh, it didn't. Let's check all our global variables and confirm
Deleted !
{ '__builtins__' : < module '__builtin__' ( built - in ) > , 'SomeClass' : < class __main__ . SomeClass at 0x7f98a1a5f668 > , '__package__' : None , '__name__' : '__main__' , '__doc__' : None }
좋아, 이제 삭제 되었나요?
del x
직접 x.__del__()
호출하지 않습니다.del x
발생하면 Python은 현재 스코프에서 이름 x
삭제하고 1 x
의 참조 수가 참조됩니다. __del__()
객체의 기준 수가 0에 도달 할 때만 호출됩니다.__del__()
는 대화식 통역사의 이전 명령문 ( >>> y
)이 동일한 객체에 대한 다른 참조를 만들었 기 때문에 호출되지 않았습니다 (특히 마지막 비 None
의 결과 값을 참조하는 _
Magic 변수 del y
발생했을 때 참조 수가 0에 도달하는 것을 방지합니다.globals
호출 (또는 실제로는 None
결과가없는 것을 실행하면 _
새로운 결과를 참조하여 기존 참조를 삭제했습니다. 이제 참조 수가 0에 도달했으며 "삭제 된!"을 볼 수 있습니다. 인쇄되고 있습니다 (마지막으로!).1.
a = 1
def some_func ():
return a
def another_func ():
a += 1
return a
2.
def some_closure_func ():
a = 1
def some_inner_func ():
return a
return some_inner_func ()
def another_closure_func ():
a = 1
def another_inner_func ():
a += 1
return a
return another_inner_func ()
산출:
> >> some_func ()
1
> >> another_func ()
UnboundLocalError : local variable 'a' referenced before assignment
>> > some_closure_func ()
1
> >> another_closure_func ()
UnboundLocalError : local variable 'a' referenced before assignment
범위가 변수에 할당되면 해당 범위에 국한됩니다. 따라서 a
another_func
의 범위로 로컬이되지만 이전에는 동일한 범위로 초기화되지 않았으므로 오류가 발생합니다.
another_func
에서 외부 스코프 변수 a
를 수정하려면 global
키워드를 사용해야합니다.
def another_func ()
global a
a += 1
return a
산출:
> >> another_func ()
2
another_closure_func
_closure_func에서 a
another_inner_func
의 범위에 국한적이지만 이전에는 동일한 범위로 초기화되지 않았으므로 오류가 발생합니다.
another_inner_func
_inner_func에서 외부 스코프 변수 a
를 수정하려면 nonlocal
키워드를 사용하십시오. 비 국소 문은 가장 가까운 외부 (글로벌 제외)로 정의 된 변수를 참조하는 데 사용됩니다.
def another_func ():
a = 1
def another_inner_func ():
nonlocal a
a += 1
return a
return another_inner_func ()
산출:
> >> another_func ()
2
Keywords global
및 nonlocal
Python 통역사에게 새로운 변수를 선언하지 말고 해당 외부 범위에서 찾아 보라고합니다.
파이썬에서 네임 스페이스와 범위 해상도가 어떻게 작동하는지에 대한 자세한 내용은 짧지 만 멋진 안내서를 읽으십시오.
list_1 = [ 1 , 2 , 3 , 4 ]
list_2 = [ 1 , 2 , 3 , 4 ]
list_3 = [ 1 , 2 , 3 , 4 ]
list_4 = [ 1 , 2 , 3 , 4 ]
for idx , item in enumerate ( list_1 ):
del item
for idx , item in enumerate ( list_2 ):
list_2 . remove ( item )
for idx , item in enumerate ( list_3 [:]):
list_3 . remove ( item )
for idx , item in enumerate ( list_4 ):
list_4 . pop ( idx )
산출:
> >> list_1
[ 1 , 2 , 3 , 4 ]
> >> list_2
[ 2 , 4 ]
> >> list_3
[]
> >> list_4
[ 2 , 4 ]
출력이 왜 [2, 4]
인지 추측 할 수 있습니까?
반복하는 객체를 바꾸는 것은 결코 좋은 생각이 아닙니다. 그렇게하는 올바른 방법은 대신 객체의 사본을 반복하는 것입니다. list_3[:]
바로 그 일을합니다.
> >> some_list = [ 1 , 2 , 3 , 4 ]
> >> id ( some_list )
139798789457608
> >> id ( some_list [:]) # Notice that python creates new object for sliced list.
139798779601192
del
, remove
및 pop
의 차이점 :
del var_name
로컬 또는 글로벌 네임 스페이스에서 var_name
의 바인딩을 제거합니다 (따라서 list_1
이 영향을받지 않는 이유).remove
특정 인덱스가 아닌 첫 번째 일치하는 값을 제거하고 값을 찾을 수없는 경우 ValueError
올리십시오.pop
특정 인덱스에서 요소를 제거하고 반환하고 유효하지 않은 인덱스가 지정되면 IndexError
올리십시오. 출력이 왜 [2, 4]
인가?
list_2
또는 list_4
에서 1
제거하면 목록의 내용이 이제 [2, 3, 4]
입니다. 나머지 요소는 아래로 이동하고, 즉, 2
인덱스 0에 있고, 3
색인 1에 있습니다. 다음 반복은 인덱스 1 ( 3
)을 살펴볼 것이므로 2
는 완전히 건너 뜁니다. 목록 시퀀스의 모든 대체 요소에서도 비슷한 일이 발생합니다. > >> numbers = list ( range ( 7 ))
> >> numbers
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
> >> first_three , remaining = numbers [: 3 ], numbers [ 3 :]
> >> first_three , remaining
([ 0 , 1 , 2 ], [ 3 , 4 , 5 , 6 ])
>> > numbers_iter = iter ( numbers )
>> > list ( zip ( numbers_iter , first_three ))
[( 0 , 0 ), ( 1 , 1 ), ( 2 , 2 )]
# so far so good, let's zip the remaining
>> > list ( zip ( numbers_iter , remaining ))
[( 4 , 3 ), ( 5 , 4 ), ( 6 , 5 )]
요소 3
은 numbers
목록에서 어디로 갔습니까?
def zip ( * iterables ):
sentinel = object ()
iterators = [ iter ( it ) for it in iterables ]
while iterators :
result = []
for it in iterators :
elem = next ( it , sentinel )
if elem is sentinel : return
result . append ( elem )
yield tuple ( result )
next
함수를 호출하여 각 항목을 result
목록에 추가하며 반복 할 수있는 사람이 소진 될 때마다 중지됩니다.result
목록의 기존 요소가 폐기됩니다. 그것이 numbers_iter
에서 3
에서 일어난 일입니다.zip
사용하여 위의 올바른 방법은 > >> numbers = list ( range ( 7 ))
> >> numbers_iter = iter ( numbers )
> >> list ( zip ( first_three , numbers_iter ))
[( 0 , 0 ), ( 1 , 1 ), ( 2 , 2 )]
> >> list ( zip ( remaining , numbers_iter ))
[( 3 , 3 ), ( 4 , 4 ), ( 5 , 5 ), ( 6 , 6 )]
1.
for x in range ( 7 ):
if x == 6 :
print ( x , ': for x inside loop' )
print ( x , ': x in global' )
산출:
6 : for x inside loop
6 : x in global
그러나 x
루프의 범위 밖에서 정의되지 않았습니다 ...
2.
# This time let's initialize x first
x = - 1
for x in range ( 7 ):
if x == 6 :
print ( x , ': for x inside loop' )
print ( x , ': x in global' )
산출:
6 : for x inside loop
6 : x in global
3.
출력 (Python 2.x) :
> >> x = 1
> >> print ([ x for x in range ( 5 )])
[ 0 , 1 , 2 , 3 , 4 ]
> >> print ( x )
4
출력 (Python 3.x) :
> >> x = 1
> >> print ([ x for x in range ( 5 )])
[ 0 , 1 , 2 , 3 , 4 ]
> >> print ( x )
1
파이썬에서, 루프는 존재하는 범위를 사용하고 정의 된 루프 변수를 뒤로 남겨 둡니다. 이는 이전에 글로벌 네임 스페이스에서 For-Loop 변수를 명시 적으로 정의한 경우에도 적용됩니다. 이 경우 기존 변수를 다시 표시합니다.
Python 2.X 및 Python 3.x 통역사의 출력 차이는 Python 3.0 Changelog의 새로운 내용에 기록 된 변경 사항에 따라 설명 할 수 있습니다.
"목록 이해력은 더 이상 구문 형식을 지원하지 않습니다
[... for var in item1, item2, ...]
[... for var in (item1, item2, ...)]
이해력은 다른 의미를 가지고 있습니다.list()
생성자 내부의 발전기 표현식에 대해 구문 설탕에 더 가깝고 특히 루프 제어 변수가 더 이상 주변 범위로 누출되지 않습니다. "
def some_func ( default_arg = []):
default_arg . append ( "some_string" )
return default_arg
산출:
> >> some_func ()
[ 'some_string' ]
> >> some_func ()
[ 'some_string' , 'some_string' ]
> >> some_func ([])
[ 'some_string' ]
> >> some_func ()
[ 'some_string' , 'some_string' , 'some_string' ]
파이썬에서 함수의 기본 변이 가능한 인수는 기능을 호출 할 때마다 초기화되지 않습니다. 대신, 최근에 할당 된 값은 기본값으로 사용됩니다. 인수로 []
some_func
으로 명시 적으로 전달하면 default_arg
변수의 기본값이 사용되지 않았으므로 함수는 예상대로 반환되었습니다.
def some_func ( default_arg = []):
default_arg . append ( "some_string" )
return default_arg
산출:
> >> some_func . __defaults__ #This will show the default argument values for the function
([],)
> >> some_func ()
> >> some_func . __defaults__
([ 'some_string' ],)
> >> some_func ()
> >> some_func . __defaults__
([ 'some_string' , 'some_string' ],)
> >> some_func ([])
> >> some_func . __defaults__
([ 'some_string' , 'some_string' ],)
변이 가능한 인수로 인해 버그를 피하기위한 일반적인 관행은 기본값으로 None
할당하지 않고 나중에 해당 인수에 해당하는 함수에 전달되는지 확인하는 것입니다. 예:
def some_func ( default_arg = None ):
if default_arg is None :
default_arg = []
default_arg . append ( "some_string" )
return default_arg
some_list = [ 1 , 2 , 3 ]
try :
# This should raise an ``IndexError``
print ( some_list [ 4 ])
except IndexError , ValueError :
print ( "Caught!" )
try :
# This should raise a ``ValueError``
some_list . remove ( 4 )
except IndexError , ValueError :
print ( "Caught again!" )
출력 (Python 2.x) :
Caught !
ValueError : list . remove ( x ): x not in list
출력 (Python 3.x) :
File "<input>" , line 3
except IndexError , ValueError :
^
SyntaxError : invalid syntax
제외 조항에 여러 예외를 추가하려면 첫 번째 인수로 괄호로 전달해야합니다. 두 번째 인수는 선택적 이름이며, 제공 될 때 제공되는 예외 인스턴스를 바인딩합니다. 예,
some_list = [ 1 , 2 , 3 ]
try :
# This should raise a ``ValueError``
some_list . remove ( 4 )
except ( IndexError , ValueError ), e :
print ( "Caught again!" )
print ( e )
출력 (Python 2.x) :
Caught again!
list.remove(x): x not in list
출력 (Python 3.x) :
File "<input>" , line 4
except ( IndexError , ValueError ), e :
^
IndentationError : unindent does not match any outer indentation level
쉼표로 변수와의 예외를 분리하는 것은 더 이상 사용되지 않으며 Python 3에서는 작동하지 않습니다. 올바른 방법은 as
사용하는 것입니다. 예,
some_list = [ 1 , 2 , 3 ]
try :
some_list . remove ( 4 )
except ( IndexError , ValueError ) as e :
print ( "Caught again!" )
print ( e )
산출:
Caught again!
list.remove(x): x not in list
1.
a = [ 1 , 2 , 3 , 4 ]
b = a
a = a + [ 5 , 6 , 7 , 8 ]
산출:
> >> a
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
> >> b
[ 1 , 2 , 3 , 4 ]
2.
a = [ 1 , 2 , 3 , 4 ]
b = a
a += [ 5 , 6 , 7 , 8 ]
산출:
> >> a
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
> >> b
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
a += b
항상 a = a + b
와 같은 방식으로 행동하는 것은 아닙니다. 클래스는 op=
연산자를 다르게 구현할 수 있으며 목록은이를 수행합니다.
표현 a = a + [5,6,7,8]
는 새 목록을 생성하고 해당 새 목록에 대한 a
를 설정하여 b
변경하지 않습니다.
표현 a += [5,6,7,8]
는 실제로 a
와 b
여전히 내내 수정 된 동일한 목록을 가리 키도록 목록에서 작동하는 "확장"함수에 매핑됩니다.
1.
x = 5
class SomeClass :
x = 17
y = ( x for i in range ( 10 ))
산출:
> >> list ( SomeClass . y )[ 0 ]
5
2.
x = 5
class SomeClass :
x = 17
y = [ x for i in range ( 10 )]
출력 (Python 2.x) :
> >> SomeClass . y [ 0 ]
17
출력 (Python 3.x) :
> >> SomeClass . y [ 0 ]
5
목록의 중간 요소를 얻기 위해 순진한 기능을 구현합시다.
def get_middle ( some_list ):
mid_index = round ( len ( some_list ) / 2 )
return some_list [ mid_index - 1 ]
Python 3.x :
> >> get_middle ([ 1 ]) # looks good
1
> >> get_middle ([ 1 , 2 , 3 ]) # looks good
2
> >> get_middle ([ 1 , 2 , 3 , 4 , 5 ]) # huh?
2
> >> len ([ 1 , 2 , 3 , 4 , 5 ]) / 2 # good
2.5
> >> round ( len ([ 1 , 2 , 3 , 4 , 5 ]) / 2 ) # why?
2
마치 파이썬이 2.5 ~ 2를 반올림 한 것처럼 보입니다.
round()
.5 분수가 가장 가까운 짝수 로 반올림되는 Banker의 반올림을 사용합니다. > >> round ( 0.5 )
0
> >> round ( 1.5 )
2
> >> round ( 2.5 )
2
> >> import numpy # numpy does the same
> >> numpy . round ( 0.5 )
0.0
> >> numpy . round ( 1.5 )
2.0
> >> numpy . round ( 2.5 )
2.0
get_middle([1])
인덱스가 round(0.5) - 1 = 0 - 1 = -1
이기 때문에 1 만 반환하여 목록의 마지막 요소를 반환합니다.나는 다음 시나리오 중 하나 이상을 발견하지 못한 단일 경험 Pythonist조차 만나지 않았습니다.
1.
x , y = ( 0 , 1 ) if True else None , None
산출:
> >> x , y # expected (0, 1)
(( 0 , 1 ), None )
2.
t = ( 'one' , 'two' )
for i in t :
print ( i )
t = ( 'one' )
for i in t :
print ( i )
t = ()
print ( t )
산출:
one
two
o
n
e
tuple ()
3.
ten_words_list = [
"some",
"very",
"big",
"list",
"that"
"consists",
"of",
"exactly",
"ten",
"words"
]
산출
> >> len ( ten_words_list )
9
4. 충분히 강하게 주장하지 않습니다
a = "python"
b = "javascript"
산출:
# An assert statement with an assertion failure message.
> >> assert ( a == b , "Both languages are different" )
# No AssertionError is raised
5.
some_list = [ 1 , 2 , 3 ]
some_dict = {
"key_1" : 1 ,
"key_2" : 2 ,
"key_3" : 3
}
some_list = some_list . append ( 4 )
some_dict = some_dict . update ({ "key_4" : 4 })
산출:
> >> print ( some_list )
None
> >> print ( some_dict )
None
6.
def some_recursive_func ( a ):
if a [ 0 ] == 0 :
return
a [ 0 ] -= 1
some_recursive_func ( a )
return a
def similar_recursive_func ( a ):
if a == 0 :
return a
a -= 1
similar_recursive_func ( a )
return a
산출:
> >> some_recursive_func ([ 5 , 0 ])
[ 0 , 0 ]
> >> similar_recursive_func ( 5 )
4
1의 경우, 예상 행동에 대한 올바른 명령문은 x, y = (0, 1) if True else (None, None)
.
2의 경우, 예상 행동에 대한 올바른 명령문은 t = ('one',)
또는 t = 'one',
(Comma가 누락) 그렇지 않으면 통역사는 t
str
로 간주하고 문자별로 그 문자를 반복합니다.
()
는 특수 토큰이며 빈 tuple
나타냅니다.
3시에 이미 알아 낸 것처럼 목록에 5 번째 요소 ( "that"
) 이후 누락 된 쉼표가 있습니다. 암시적인 문자열 문자 그대로 연결에 의해
> >> ten_words_list
[ 'some' , 'very' , 'big' , 'list' , 'thatconsists' , 'of' , 'exactly' , 'ten' , 'words' ]
개별 표현 a == b
를 주장하는 대신 전체 튜플을 주장하기 때문에 4 번째 스 니펫에서 AssertionError
제기되지 않았습니다. 다음 스 니펫은 물건을 정리할 것입니다.
> >> a = "python"
> >> b = "javascript"
> >> assert a == b
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AssertionError
>> > assert ( a == b , "Values are not equal" )
< stdin > : 1 : SyntaxWarning : assertion is always true , perhaps remove parentheses ?
>> > assert a == b , "Values are not equal"
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AssertionError : Values are not equal
다섯 번째 스 니펫의 경우 list.append
, dict.update
None
list.sort
등과 같은 시퀀스/매핑 객체의 항목을 수정하는 대부분의 방법. 이에 대한 이론적 근거는 작업을 장소에서 수행 할 수있는 경우 (여기서 참조) 개체의 사본을 피함으로써 성능을 향상시키는 것입니다.
마지막으로는 명백해야하며, 목록과 같은 변이 가능한 객체 (예 : list
)가 변경 될 수 있어야하며, 불변의 재 할당 ( a -= 1
)은 값의 변경이 아닙니다.
이러한 nitpicks를 알고 있으면 장기적으로 몇 시간의 디버깅 노력을 절약 할 수 있습니다.
> >> 'a' . split ()
[ 'a' ]
# is same as
> >> 'a' . split ( ' ' )
[ 'a' ]
# but
> >> len ( '' . split ())
0
# isn't the same as
> >> len ( '' . split ( ' ' ))
1
' '
공간 인 것처럼 보일 수 있지만 문서에 따라SEP가 지정되지 않았거나
None
다른 분할 알고리즘이 적용됩니다. 연속 공백의 실행은 단일 분리기로 간주되며 결과에는 시작 또는 끝에 흰색이 흰색을 선도하는 경우 비어있는 문자열이 포함되어 있지 않습니다. 결과적으로, 빈 문자열이나 분리기가 없음으로 구성된 공백으로 구성된 문자열을 분할합니다[]
. SEP가 주어지면 연속 구분자는 함께 그룹화되지 않으며 빈 줄 (예 :'1,,2'.split(',')
리턴['1', '', '2']
)를 지시하는 것으로 간주됩니다. 지정된 분리기로 빈 문자열을 분할하면['']
.
> >> ' a ' . split ( ' ' )
[ '' , 'a' , '' ]
> >> ' a ' . split ()
[ 'a' ]
> >> '' . split ( ' ' )
[ '' ]
# File: module.py
def some_weird_name_func_ ():
print ( "works!" )
def _another_weird_name_func ():
print ( "works!" )
산출
> >> from module import *
> >> some_weird_name_func_ ()
"works!"
> >> _another_weird_name_func ()
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
NameError : name '_another_weird_name_func' is not defined
와일드 카드 수입을 사용하지 않는 것이 좋습니다. 이에 대한 첫 번째 명백한 이유는 와일드 카드 수입에서 선행 밑줄이있는 이름이 가져 오지 않기 때문입니다. 이로 인해 런타임 중에 오류가 발생할 수 있습니다.
우리가 from ... import a, b, c
사용했다면, 위의 NameError
발생하지 않았을 것입니다.
> >> from module import some_weird_name_func_ , _another_weird_name_func
> >> _another_weird_name_func ()
works !
와일드 카드 가져 오기를 실제로 사용하려면 모듈에서 __all__
목록을 정의해야합니다.
__all__ = [ '_another_weird_name_func' ]
def some_weird_name_func_ ():
print ( "works!" )
def _another_weird_name_func ():
print ( "works!" )
산출
> >> _another_weird_name_func ()
"works!"
> >> some_weird_name_func_ ()
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
NameError : name 'some_weird_name_func_' is not defined
> >> x = 7 , 8 , 9
> >> sorted ( x ) == x
False
> >> sorted ( x ) == sorted ( x )
True
> >> y = reversed ( x )
> >> sorted ( y ) == sorted ( y )
False
sorted
메소드는 항상 목록을 반환하고 목록과 튜플을 비교하면 항상 파이썬에서 False
반환합니다.
> >> [] == tuple ()
False
> >> x = 7 , 8 , 9
> >> type ( x ), type ( sorted ( x ))
( tuple , list )
sorted
것과는 달리, reversed
메소드는 반복자를 반환합니다. 왜? 정렬을 위해서는 반복기가 내내 수정되거나 추가 컨테이너 (목록)를 사용해야하므로 반전은 마지막 인덱스에서 첫 인덱스로 반복하여 단순히 작동 할 수 있습니다.
따라서 sorted(y) == sorted(y)
비교하는 동안 sorted()
에 대한 첫 번째 호출은 반복자 y
소비하고 다음 호출은 빈 목록을 반환합니다.
> >> x = 7 , 8 , 9
> >> y = reversed ( x )
> >> sorted ( y ), sorted ( y )
([ 7 , 8 , 9 ], [])
from datetime import datetime
midnight = datetime ( 2018 , 1 , 1 , 0 , 0 )
midnight_time = midnight . time ()
noon = datetime ( 2018 , 1 , 1 , 12 , 0 )
noon_time = noon . time ()
if midnight_time :
print ( "Time at midnight is" , midnight_time )
if noon_time :
print ( "Time at noon is" , noon_time )
출력 (<3.5) :
( 'Time at noon is' , datetime . time ( 12 , 0 ))
자정 시간은 인쇄되지 않습니다.
Python 3.5 이전에는 datetime.time
객체의 부울 값이 UTC에서 자정을 나타내는 경우 False
으로 간주되었습니다. if obj:
구문을 사용할 때 오류가 발생하기 위해 오류가 발생하여 obj
가 NULL인지 또는 "빈"과 동등한 지 확인합니다.
이 섹션에는 나와 같은 대부분의 초보자가 알지 못하는 파이썬에 대한 몇 가지 덜 알려져 있고 흥미로운 것들이 포함되어 있습니다 (더 이상은 아닙니다).
글쎄, 당신은 간다
import antigravity
출력 : SSHH ... 슈퍼 비밀입니다.
antigravity
모듈은 Python 개발자가 발표 한 몇 안되는 부활절 달걀 중 하나입니다.import antigravity
Python에 대한 클래식 XKCD 만화를 가리키는 웹 브라우저를 열어줍니다.goto
,하지만 왜? from goto import goto , label
for i in range ( 9 ):
for j in range ( 9 ):
for k in range ( 9 ):
print ( "I am trapped, please rescue!" )
if k == 2 :
goto . breakout # breaking out from a deeply nested loop
label . breakout
print ( "Freedom!" )
출력 (Python 2.3) :
I am trapped , please rescue !
I am trapped , please rescue !
Freedom !
goto
의 작업 버전은 2004 년 4 월 1 일에 April Fool의 농담으로 발표되었습니다.goto
Python에 존재하지 않는 이유는 다음과 같습니다.파이썬에서 공백을 사용하여 스코프를 나타내는 것을 좋아하지 않는 사람들 중 하나라면, 가져 오기, C 스타일 {}를 사용할 수 있습니다.
from __future__ import braces
산출:
File "some_file.py" , line 1
from __future__ import braces
SyntaxError : not a chance
바지 멜빵? 안 돼요! 그것이 실망 스럽다고 생각되면 Java를 사용하십시오. 좋아요, 또 다른 놀라운 일, __future__
모듈 코드에서 SyntaxError
제기 된 곳은 어디입니까?
__future__
모듈은 일반적으로 미래 버전의 Python의 기능을 제공하는 데 사용됩니다. 그러나이 특정 맥락에서 "미래"는 아이러니하다.future.c
파일.future.c
적절한 코드를 실행하기 전에 정상적인 가져 오기 명령문으로 취급합니다.출력 (Python 3.x)
> >> from __future__ import barry_as_FLUFL
> >> "Ruby" != "Python" # there's no doubt about it
File "some_file.py" , line 1
"Ruby" != "Python"
^
SyntaxError : invalid syntax
>> > "Ruby" <> "Python"
True
간다.
이것은 2009 년 4 월 1 일에 발표 된 PEP-401과 관련이 있습니다 (이제 무엇을 의미하는지).
PEP-401에서 인용
Python 3.0의 불평등 연산자가 끔찍하고 손가락을 유발하는 실수라는 것을 인식하여 독감은 <> 다이아몬드 연산자를 유일한 철자로 회복시킵니다.
배리 삼촌이 PEP에서 나누어야 할 것이 더 많았습니다. 여기서 읽을 수 있습니다.
대화식 환경에서 잘 작동하지만 Python 파일을 통해 실행할 때 SyntaxError
발생합니다 (이 문제 참조). 그러나 eval
내부에 진술을 마무리하거나 컴파일을 작동시키기 위해 compile
할 수 있습니다.
from __future__ import barry_as_FLUFL
print ( eval ( '"Ruby" <> "Python"' ))
import this
잠깐, 이게 뭐야? this
사랑입니다
산출:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
파이썬의 선입니다!
> >> love = this
> >> this is love
True
> >> love is True
False
> >> love is False
False
> >> love is not True or False
True
> >> love is not True or False ; love is love # Love is complicated
True
this
Python의 선을위한 부활절 달걀입니다 (PEP 20).love is not True or False; love is love
, 아이러니하지만, 자기 설명이 필요합니다 ( is not
않다면 is
와 관련된 예제를 참조하십시오). 루프의 else
조항. 전형적인 예는 다음과 같습니다.
def does_exists_num ( l , to_find ):
for num in l :
if num == to_find :
print ( "Exists!" )
break
else :
print ( "Does not exist" )
산출:
> >> some_list = [ 1 , 2 , 3 , 4 , 5 ]
> >> does_exists_num ( some_list , 4 )
Exists !
>> > does_exists_num ( some_list , - 1 )
Does not exist
예외 처리의 else
절. 예를 들어
try :
pass
except :
print ( "Exception occurred!!!" )
else :
print ( "Try block executed successfully..." )
산출:
Try block executed successfully ...
else
절은 모든 반복 후 명시적인 break
없는 경우에만 실행됩니다. 당신은 그것을 "nobreak"조항으로 생각할 수 있습니다.else
Clause는 try
Cound에서 else
조항에 도달하면 시도 블록이 실제로 성공적으로 완료되었음을 의미하므로 시도 블록이 "완료 조항"이라고도합니다. def some_func ():
Ellipsis
산출
> >> some_func ()
# No output, No Error
> >> SomeRandomString
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
NameError : name 'SomeRandomString' is not defined
> >> Ellipsis
Ellipsis
Ellipsis
는 전 세계적으로 사용 가능한 내장 객체로 ...
에 해당합니다. > >> ...
Ellipsis
pass
문과 마찬가지로) > >> import numpy as np
> >> three_dimensional_array = np . arange ( 8 ). reshape ( 2 , 2 , 2 )
array ([
[
[ 0 , 1 ],
[ 2 , 3 ]
],
[
[ 4 , 5 ],
[ 6 , 7 ]
]
])
three_dimensional_array
는 배열 배열 배열입니다. 가장 안쪽 배열의 두 번째 요소 (인덱스 1
)를 인쇄하고 싶다고 가정 해 봅시다. 앞의 모든 차원을 우회하기 위해 타원을 사용하여 > >> three_dimensional_array [:,:, 1 ]
array ([[ 1 , 3 ],
[ 5 , 7 ]])
> >> three_dimensional_array [..., 1 ] # using Ellipsis.
array ([[ 1 , 3 ],
[ 5 , 7 ]])
n_dimensional_array[firs_dim_slice, ..., last_dim_slice]
).(Callable[..., int]
또는 Tuple[str, ...]
)을 나타냅니다.철자가 의도 된 것입니다. 제발, 이것을 위해 패치를 제출하지 마십시오.
출력 (Python 3.x) :
> >> infinity = float ( 'infinity' )
> >> hash ( infinity )
314159
> >> hash ( float ( '-inf' ))
- 314159
float('-inf')
는 Python 3에서 "-10 x π"인 반면, Python 2의 "-10⁵ xe"입니다.1.
class Yo ( object ):
def __init__ ( self ):
self . __honey = True
self . bro = True
산출:
> >> Yo (). bro
True
> >> Yo (). __honey
AttributeError : 'Yo' object has no attribute '__honey'
> >> Yo (). _Yo__honey
True
2.
class Yo ( object ):
def __init__ ( self ):
# Let's try something symmetrical this time
self . __honey__ = True
self . bro = True
산출:
> >> Yo (). bro
True
> >> Yo (). _Yo__honey__
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AttributeError : 'Yo' object has no attribute '_Yo__honey__'
Yo()._Yo__honey
왜 일했습니까?
3.
_A__variable = "Some value"
class A ( object ):
def some_func ( self ):
return __variable # not initialized anywhere yet
산출:
> >> A (). __variable
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AttributeError : 'A' object has no attribute '__variable'
> >> A (). some_func ()
'Some value'
__
(Double Outscore 일명 "dunder")로 시작하는 클래스 멤버 이름을 수정하고 _NameOfTheClass
추가하여 하나 이상의 후행 밑줄로 끝나지 않습니다.__honey
속성에 액세스하기 위해 _Yo
전면에 추가해야했는데, 이는 다른 클래스에 정의 된 동일한 이름 속성과의 충돌을 방지 할 수있었습니다.__variable
의 이름 __variable return __variable
_A__variable
로 엉망이되었으며, 이는 외부 범위에서 선언 한 변수의 이름이기도합니다.산출:
> >> value = 11
> >> valuе = 32
> >> value
11
wut?
참고 : 이것을 재현하는 가장 쉬운 방법은 위의 스 니펫의 진술을 간단히 복사하여 파일/쉘에 붙여 넣는 것입니다.
일부 비 서구 캐릭터는 영어 알파벳의 문자와 동일하게 보이지만 통역사에 의해 구별됩니다.
> >> ord ( 'е' ) # cyrillic 'e' (Ye)
1077
> >> ord ( 'e' ) # latin 'e', as used in English and typed using standard keyboard
101
> >> 'е' == 'e'
False
> >> value = 42 # latin e
> >> valuе = 23 # cyrillic 'e', Python 2.x interpreter would raise a `SyntaxError` here
> >> value
42
내장형 ord()
함수는 문자의 유니 코드 코드 포인트를 반환하고, 키릴 릭 'e'및 라틴어 'e'의 다른 코드 위치는 위의 예의 동작을 정당화합니다.
# `pip install numpy` first.
import numpy as np
def energy_send ( x ):
# Initializing a numpy array
np . array ([ float ( x )])
def energy_receive ():
# Return an empty numpy array
return np . empty ((), dtype = np . float ). tolist ()
산출:
> >> energy_send ( 123.456 )
> >> energy_receive ()
123.456
Nobwer Prize는 어디에 있습니까?
energy_send
함수에서 생성 된 Numpy 배열은 리턴되지 않으므로 메모리 공간을 자유롭게 재 할당 할 수 있습니다.numpy.empty()
다음 무료 메모리 슬롯을 다시 구체화하지 않고 반환합니다. 이 메모리 지점은 방금 해방 된 것과 동일합니다 (일반적으로 항상 그런 것은 아닙니다). def square ( x ):
"""
A simple function to calculate the square of a number by addition.
"""
sum_so_far = 0
for counter in range ( x ):
sum_so_far = sum_so_far + x
return sum_so_far
출력 (Python 2.x) :
> >> square ( 10 )
10
100이어야합니까?
참고 : 이를 재현 할 수 없으면 쉘을 통해 Mixed_tabs_and_spaces.py 파일을 실행해보십시오.
탭과 공백을 혼합하지 마십시오! 반환의 바로 앞의 캐릭터는 "탭"이며 코드는 예제의 다른 곳에서 "4 공간"의 배수로 들여 쓰기됩니다.
이것이 Python이 탭을 처리하는 방법입니다.
첫째, 탭은 (왼쪽에서 오른쪽으로) 1 ~ 8 개의 공백으로 교체되어 교체품을 포함하는 총 문자 수는 8 <... ...>의 배수입니다.
따라서 마지막 square
함수의 마지막 줄에서 "탭"은 8 개의 공간으로 대체되어 루프로 들어갑니다.
Python 3은 그러한 경우에 자동으로 오류를 던질 정도로 친절합니다.
출력 (Python 3.x) :
TabError : inconsistent use of tabs and spaces in indentation
+=
더 빠릅니다 # using "+", three strings:
> >> timeit . timeit ( "s1 = s1 + s2 + s3" , setup = "s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000" , number = 100 )
0.25748300552368164
# using "+=", three strings:
> >> timeit . timeit ( "s1 += s2 + s3" , setup = "s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000" , number = 100 )
0.012188911437988281
s1 += s2 + s3
의 s1
)이 완전한 문자열을 계산하는 동안 파괴되지 않기 때문에 +=
두 줄 이상의 문자열을 연결하는 데 +
보다 빠릅니다. def add_string_with_plus ( iters ):
s = ""
for i in range ( iters ):
s += "xyz"
assert len ( s ) == 3 * iters
def add_bytes_with_plus ( iters ):
s = b""
for i in range ( iters ):
s += b"xyz"
assert len ( s ) == 3 * iters
def add_string_with_format ( iters ):
fs = "{}" * iters
s = fs . format ( * ([ "xyz" ] * iters ))
assert len ( s ) == 3 * iters
def add_string_with_join ( iters ):
l = []
for i in range ( iters ):
l . append ( "xyz" )
s = "" . join ( l )
assert len ( s ) == 3 * iters
def convert_list_to_string ( l , iters ):
s = "" . join ( l )
assert len ( s ) == 3 * iters
산출:
# Executed in ipython shell using %timeit for better readability of results.
# You can also use the timeit module in normal python shell/scriptm=, example usage below
# timeit.timeit('add_string_with_plus(10000)', number=1000, globals=globals())
> >> NUM_ITERS = 1000
> >> % timeit - n1000 add_string_with_plus ( NUM_ITERS )
124 µs ± 4.73 µs per loop ( mean ± std . dev . of 7 runs , 100 loops each )
> >> % timeit - n1000 add_bytes_with_plus ( NUM_ITERS )
211 µs ± 10.5 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_format ( NUM_ITERS )
61 µs ± 2.18 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_join ( NUM_ITERS )
117 µs ± 3.21 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> l = [ "xyz" ] * NUM_ITERS
> >> % timeit - n1000 convert_list_to_string ( l , NUM_ITERS )
10.1 µs ± 1.06 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
반복 횟수를 10 배로 늘리겠습니다.
> >> NUM_ITERS = 10000
> >> % timeit - n1000 add_string_with_plus ( NUM_ITERS ) # Linear increase in execution time
1.26 ms ± 76.8 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_bytes_with_plus ( NUM_ITERS ) # Quadratic increase
6.82 ms ± 134 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_format ( NUM_ITERS ) # Linear increase
645 µs ± 24.5 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_join ( NUM_ITERS ) # Linear increase
1.17 ms ± 7.25 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> l = [ "xyz" ] * NUM_ITERS
> >> % timeit - n1000 convert_list_to_string ( l , NUM_ITERS ) # Linear increase
86.3 µs ± 2 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
이 링크에서 Timeit 또는 %Timeit에 대한 자세한 내용을 읽을 수 있습니다. 코드 조각의 실행 시간을 측정하는 데 사용됩니다.
긴 문자열을 생성하는 데 +
사용하지 마십시오 - 파이썬에서 str
불변이 아니므로 왼쪽과 오른쪽 문자열은 모든 연결 쌍에 대해 새 문자열로 복사해야합니다. 길이 10의 4 개 문자열을 연결하면 (10+10)+((10+10) +10)+(((10+10) +10) +10) = 90자가 40 대신 복사됩니다. 문자. 문자열의 숫자와 크기가 증가함에 따라 상황이 차별적으로 악화됩니다 ( add_bytes_with_plus
함수의 실행 시간으로 정당화됨)
따라서 .format.
또는 %
구문 (그러나 매우 짧은 문자열의 경우 +
보다 약간 느립니다).
또는 이미 반복 가능한 객체의 형태로 사용할 수있는 내용을 사용하는 경우 훨씬 더 빠른 ''.join(iterable_object)
을 사용하십시오.
이전 예제에서 논의 된 +=
최적화로 인해 add_bytes_with_plus
와 달리 add_string_with_plus
실행 시간의 2 차 증가를 나타내지 않았습니다. 진술이 s s += "xyz"
대신 s = s + "x" + "y" + "z"
인 경우, 증가는 2 차 였을 것입니다.
def add_string_with_plus ( iters ):
s = ""
for i in range ( iters ):
s = s + "x" + "y" + "z"
assert len ( s ) == 3 * iters
> >> % timeit - n100 add_string_with_plus ( 1000 )
388 µs ± 22.4 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n100 add_string_with_plus ( 10000 ) # Quadratic increase in execution time
9 ms ± 298 µs per loop ( mean ± std . dev . of 7 runs , 100 loops each )
거대한 문자열을 형식화하고 만들 수있는 많은 방법은 파이썬의 선과 대조적으로 다소 다소 있습니다.
이를 수행하는 방법은 하나이며, 바람직하게는 하나뿐입니다.
dict
Lookups 속도 속도 * some_dict = { str ( i ): 1 for i in range ( 1_000_000 )}
another_dict = { str ( i ): 1 for i in range ( 1_000_000 )}
산출:
> >> % timeit some_dict [ '5' ]
28.6 ns ± 0.115 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
> >> some_dict [ 1 ] = 1
> >> % timeit some_dict [ '5' ]
37.2 ns ± 0.265 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
> >> % timeit another_dict [ '5' ]
28.5 ns ± 0.142 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
> >> another_dict [ 1 ] # Trying to access a key that doesn't exist
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
KeyError : 1
> >> % timeit another_dict [ '5' ]
38.5 ns ± 0.0913 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
동일한 조회가 느려지는 이유는 무엇입니까?
str
, int
, all 객체 ...)를 처리하는 일반적인 사전 조회 함수와 str
-only 키로 구성된 사전의 공용 사례에 대한 특수한 키를 처리합니다.lookdict_unicode
라는 이름으로 명명)는 모든 기존 키 (룩업 키 포함)가 문자열임을 알고 있으며 __eq__
방법을 호출하는 대신 더 빠르고 간단한 문자열 비교를 사용합니다.str
키로 dict
인스턴스에 처음 액세스 할 때는 향후 조회가 일반 기능을 사용하므로 수정됩니다.dict
인스턴스에 대해 가역적이지 않으며 키는 사전에 존재할 필요조차 없습니다. 그렇기 때문에 조회에 실패한 시도가 같은 효과가 있습니다.dict
S * import sys
class SomeClass :
def __init__ ( self ):
self . some_attr1 = 1
self . some_attr2 = 2
self . some_attr3 = 3
self . some_attr4 = 4
def dict_size ( o ):
return sys . getsizeof ( o . __dict__ )
출력 : (Python 3.8, 다른 Python 3 버전은 약간 다를 수 있습니다)
> >> o1 = SomeClass ()
> >> o2 = SomeClass ()
> >> dict_size ( o1 )
104
> >> dict_size ( o2 )
104
> >> del o1 . some_attr1
> >> o3 = SomeClass ()
> >> dict_size ( o3 )
232
> >> dict_size ( o1 )
232
다시 시도해 봅시다 ... 새로운 통역사에서 :
> >> o1 = SomeClass ()
> >> o2 = SomeClass ()
> >> dict_size ( o1 )
104 # as expected
> >> o1 . some_attr5 = 5
> >> o1 . some_attr6 = 6
> >> dict_size ( o1 )
360
> >> dict_size ( o2 )
272
> >> o3 = SomeClass ()
> >> dict_size ( o3 )
232
그 사전이 부풀어 오르는 것은 무엇입니까? 그리고 새로 생성 된 물체도 왜 부풀어 오르나요?
__init__
에 많은 속성을 추가 할 수 있습니다. "쇼어"). 크기 조정이 발생할 때 여러 인스턴스가 존재하는 경우, 동일한 클래스의 모든 미래 인스턴스에 대해 키 공유가 비활성화됩니다. Cpython은 인스턴스가 더 이상 동일한 속성 세트를 사용하고 있는지 알 수 없으며 공유 시도를 시도하기로 결정합니다. 열쇠.__init__
의 모든 속성을 초기화해야합니다! join()
목록 조작 대신 문자열 조작입니다. (첫 번째 사용에 반 직관적 인 종류)
설명 : join()
문자열의 메소드 인 경우 반복 가능한 모든 (목록, 튜플, 반복자)에서 작동 할 수 있습니다. 목록의 메소드 인 경우 모든 유형별로 별도로 구현해야합니다. 또한 일반 list
객체 API에 문자열 별 메소드를 넣는 것은 의미가 없습니다.
이상하게 보이지만 의미 적으로 올바른 진술은 거의 없습니다.
[] = ()
의미 적으로 올바른 문장입니다 (빈 tuple
을 빈 list
으로 포장하지 않음)'a'[0][0][0][0][0]
은 C에서 분기 된 다른 언어와 같은 문자 데이터 유형이 없기 때문에 의미 적으로 정확합니다. 따라서 문자열에서 단일 문자를 선택하면 a를 반환합니다. 단일 문자열.3 --0-- 5 == 8
및 --5 == 5
는 의미 적으로 올바른 진술이며 True
으로 평가됩니다. a
숫자, ++a
와 --a
는 모두 유효한 Python 문이지만 C, C ++ 또는 Java와 같은 언어의 비슷한 문장과 비교할 때 동일한 방식으로 행동하지 않습니다.
> >> a = 5
> >> a
5
> >> + + a
5
> >> - - a
5
설명:
++
운영자가 없습니다. 실제로 두 +
운영자입니다.++a
+(+a)
a
구문 분석. 마찬가지로, --a
의 출력은 정당화 될 수 있습니다.Python의 Walrus 연산자를 알고 있어야합니다. 그러나 Space Invader 운영자 에 대해 들어 본 적이 있습니까?
> >> a = 42
> >> a -= - 1
> >> a
43
다른 것과 함께 대체 증분 연산자로 사용됩니다.
> >> a += + 1
> >> a
> >> 44
설명 : 이 장난은 Raymond Hettinger의 트윗에서 나옵니다. 공간 침입자 연산자는 실제로 널 포맷 된 a -= (-1)
입니다. 이는 a = a - (- 1)
와 같습니다. a += (+ 1)
케이스에 대해 유사합니다.
Python에는 문서화되지 않은 대화 내용 연산자가 있습니다.
> >> False ** False == True
True
> >> False ** True == False
True
> >> True ** False == True
True
> >> True ** True == True
True
설명 : False
와 True
0과 1으로 대체하고 수학을 수행하면 진실 테이블은 대화 연산자와 동일합니다. (원천)
우리는 연산자에게 이야기하고 있기 때문에 매트릭스 곱셈을위한 @
연산자도 있습니다 (걱정하지 마십시오. 이번에는 실제입니다).
> >> import numpy as np
> >> np . array ([ 2 , 2 , 2 ]) @ np . array ([ 7 , 8 , 8 ])
46
설명 : @
운영자는 과학계를 염두에두고 Python 3.5에 추가되었습니다. 모든 객체는 __matmul__
매직 메소드를 과부하 하여이 연산자의 동작을 정의 할 수 있습니다.
Python 3.8에서 나중에 빠른 디버깅을 위해 f'{some_var=}
와 같은 일반적인 f- 스트링 구문을 사용할 수 있습니다. 예,
> >> some_string = "wtfpython"
> >> f' { some_string = } '
"some_string='wtfpython'"
Python은 함수의 로컬 가변 저장에 2 바이트를 사용합니다. 이론적으로 이것은 65536 변수 만 함수에서 정의 될 수 있음을 의미합니다. 그러나 Python에는 2^16 개 이상의 변수 이름을 저장하는 데 사용할 수있는 편리한 솔루션이 있습니다. 다음 코드는 65536 개 이상의 로컬 변수가 정의 될 때 스택에서 발생하는 일을 보여줍니다 (경고 :이 코드는 2^18 줄의 텍스트 라인을 인쇄하므로 준비하십시오!).
import dis
exec ( """
def f():
""" + """
""" . join ([ "X" + str ( x ) + "=" + str ( x ) for x in range ( 65539 )]))
f ()
print ( dis . dis ( f ))
여러 파이썬 스레드는 동시에 파이썬 코드를 실행하지 않습니다 (예, 올바르게 들었습니다!). 여러 스레드를 스폰하고 파이썬 코드를 동시에 실행하는 것이 직관적 인 것처럼 보일 수 있지만, Python의 글로벌 통역사 잠금으로 인해 스레드가 동일한 코어 턴에서 실행하는 것입니다. Python 스레드는 IO- 결합 작업에 적합하지만 CPU 결합 작업에 대한 Python에서 실제 병렬화를 달성하려면 Python Multiprocessing 모듈을 사용해야 할 수도 있습니다.
때로는 print
방법이 즉시 값을 인쇄하지 못할 수도 있습니다. 예를 들어,
# File some_file.py
import time
print ( "wtfpython" , end = "_" )
time . sleep ( 3 )
이렇게하면 출력 버퍼가 n
이 발생한 후 또는 프로그램이 실행을 완료 할 때 출력 버퍼가 플러시되기 때문에 end
인수로 인해 3 초 후에 wtfpython
을 인쇄합니다. flush=True
인수를 통과시켜 버퍼를 플러시하도록 강제 할 수 있습니다.
범위를 벗어난 지수로 슬라이스 목록이 오류가 발생하지 않습니다.
> >> some_list = [ 1 , 2 , 3 , 4 , 5 ]
> >> some_list [ 111 :]
[]
반복적 인 슬라이스가 항상 새로운 객체를 생성하는 것은 아닙니다. 예를 들어,
> >> some_str = "wtfpython"
> >> some_list = [ 'w' , 't' , 'f' , 'p' , 'y' , 't' , 'h' , 'o' , 'n' ]
> >> some_list is some_list [:] # False expected because a new object is created.
False
> >> some_str is some_str [:] # True because strings are immutable, so making a new object is of not much use.
True
Python 3에서 int('١٢٣٤٥٦٧٨٩')
123456789
반환합니다. Python에서는 소수점 문자에는 숫자 문자가 포함되며, u+0660, 아라비아 인디 숫자 0 (예 : u+0660)을 형성하는 데 사용할 수있는 모든 문자가 포함됩니다. 파이썬 의이 행동과 관련된 흥미로운 이야기가 있습니다.
Python 3에서 밑줄 (더 나은 가독성)으로 숫자 리터럴을 분리 할 수 있습니다.
> >> six_million = 6_000_000
> >> six_million
6000000
> >> hex_address = 0xF00D_CAFE
> >> hex_address
4027435774
'abc'.count('') == 4
. 다음은 count
방법의 대략적인 구현이 있습니다.
def count ( s , sub ):
result = 0
for i in range ( len ( s ) + 1 - len ( sub )):
result += ( s [ i : i + len ( sub )] == sub )
return result
동작은 원래 문자열에서 길이 0 조각이있는 빈 하위 문자 ( ''
)의 일치로 인한 것입니다.
wtfpython에 기여할 수있는 몇 가지 방법,
자세한 내용은 Contributing.md를 참조하십시오. 사물을 논의하기 위해 새로운 이슈를 만들면 자유롭게 생각하십시오.
추신 : 백 링크 요청으로 연락하지 마십시오. 프로젝트와 관련이 없다면 링크가 추가되지 않습니다.
이 컬렉션의 아이디어와 디자인은 처음에 Denys Dovhan의 멋진 프로젝트 WTFJ에서 영감을 받았습니다. Pythonistas의 압도적 인 지원은 지금의 모양을 주었다.
© Satwik Kansal
If you like wtfpython, you can use these quick links to share it with your friends,
트위터 | 링크드인 | 페이스북
I've received a few requests for the pdf (and epub) version of wtfpython. You can add your details here to get them as soon as they are finished.
그게 전부입니다! For upcoming content like this, you can add your email here.