Pythonのレアな知識ポイントのまとめ
この記事はhttps://github.com/leisurelicht/wtfpython-cnから多くを借用してい ます
□except ... finally:プログラム全体が正常に実行されたかどうかに関係なく、finallyのすべてのロジックが実行されます。同時に、finallyのreturnは、前のロジックの実行結果を上書きし、例外も無視されます。したがって、最終的にはリソース関連のロジックのみを追加することをお勧めします
def calculate(division):
try:
return 100 / division
except ZeroDivisionError as e:
raise ValueError("Invalid inputs")
finally:
return 0
print(calculate(0))
'''
最后不会报错,而是正常退出返回 0
'''
□for ... in:for ... in操作は、最初に__iter__を呼び出してイテレーターを取得し、次に__next__を呼び出して要素に順番にアクセスします。したがって、反復処理中に要素の内容が変更されると、__ next__がアドレスビットに従って要素にアクセスするため、異常な状況が発生します。
ここには別の知識ポイントがあります:iterable iterable contains iterator、およびiterator contains generator。同時に、ループすることができる限り、反復可能であり、反復子は次に要素に順番にアクセスできます。これが反復子をトラバースする唯一の便利な方法です。写真で簡単に説明しましょう。
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)
Output:
>>> list_1
[1, 2, 3, 4]
>>> list_2
[2, 4]
>>> list_3
[]
>>> list_4
[2, 4]
'''
del, remove 和 pop 的不同:
del var_name 只是从本地或全局命名空间中删除了 var_name (这就是为什么 list_1 没有受到影响).
remove 会删除第一个匹配到的指定值, 而不是特定的索引, 如果找不到值则抛出 ValueError 异常.
pop 则会删除指定索引处的元素并返回它, 如果指定了无效的索引则抛出 IndexError 异常.
'''
for i in range(4):
print(i)
i = 10
# 在每次迭代开始之前, 迭代器(这里指 range(4)) 生成的下一个元素就被解包并赋值给目标列表的变量(这里指 i)了.
'''
output:
0
1
2
3
'''
□python =:pythonの代入演算は、変数オブジェクトの単純な参照代入であり、2つの参照は実際には同じメモリオブジェクトを指します。
list1 = [1, 2, 3]
list2 = list1
list2[0] = 6
'''
list1 = [6, 2, 3]
list2 = [6, 2, 3]
'''
□リストが空である:PEP 8でリストが空でないか空でないかを判断するには、リストではなくリストを直接使用することをお勧めします。
□関数のデフォルトパラメータとして変数オブジェクトを使用:パラメータのデフォルト値は、メソッド定義の実行時にすでに設定されています。つまり、デフォルト値は一度だけ設定されます。関数が定義されると、関数が呼び出されるたびに設定されます「事前計算」プロセスがあります。この時点で、変数オブジェクトが関数内で変更された場合、その後のデフォルト入力は変更されたオブジェクトになります
#!/usr/bin/env python
# coding: utf-8
class Test(Object):
def process_data(self, data=[]):
# 排序
data.sort()
# 追加结束符
data.append("End")
return data
test1 = Test()
print(test1.process_data())
test2 = Test()
print(test2.process_data())
test3 = Test()
print(test3.process_data(data=["Name:123", "Age:34"]))
test4 = Test()
print(test4.process_data())
'''
输出:
['End']
['End', 'End']
["Age:34", "Name:123", 'End']
['End', 'End', 'End']
'''
変数オブジェクトをデフォルトにする必要がある場合は、次のメソッドを使用できます
def some_func(default_arg=None):
if default_arg is None:
default_arg = []
default_arg.append("some_string")
return default_arg
□非ローカル:非ローカルは、現在のスコープ外の1つのレベルでのみローカル変数を取得でき、その拡張レベルは1のみです。
n = 0
def f():
def inner():
nonlocal n
n = 2
n = 1
print(n)
inner()
print(n)
if __name__ == "__main__":
f()
print()
'''
输出:
1
2
0
'''
□リストスライス:リストをスライスする場合、ストップはスタートの論理的な右側にある必要があります。そうでない場合、出力は空になります
TempStr = 'Hello World'
print(TempStr[-5:-1])
print(TempStr[-5:0])
print(TempStr[-4:-1])
print(TempStr[-5:])
'''
输出:
Worl
orl
World
'''
□__all__:__all__は、from ... import *によってエクスポートされるオブジェクトのみを制限できます。現時点では、__ all__で指定されたオブジェクトのみをエクスポートできます。ただし、すべてのオブジェクトは、from ... import ...を使用して明示的にエクスポートできます。
□クロージャ値の転送:クロージャを介して渡された値はオブジェクトアドレスを介して渡されるため、入力値が外部で変更された場合、クロージャ内の結果も影響を受けます。ただし、着信値が内部で直接変更される場合、ローカルスコープのクロージャの着信値は無効になり、コピーされないように要求される場合があります。このとき、非ローカルを使用してクロージャの値を指定すると、クロージャ内の外部変数を変更できます。
#!/usr/bin/env python
# coding: utf-8
def a(s):
def b():
print(i)
# i = 2 # 添加该行,由于指定了局部变量,对应闭包传递过来的对象引用被覆盖,将调试在赋值前引用
# print(i)
i = s
print(i)
return b
if __name__ == '__main__':
tb1 = a(1)
tb1()
tb2 = a(2)
tb1()
tb2()
'''
输出:
1
1
2
1
2
'''
□クロージャー値の転送:ループ内で関数を定義する場合、関数が本体でループ変数を使用すると、クロージャー関数はその値ではなくループ変数にバインドされます。したがって、すべての関数は変数に割り当てられた最後の値を使用して計算されます。
def f():
t = [lambda x: i*x for i in range(4)]
return t
print([M(2) for M in f()])
'''
output:
[6, 6, 6, 6]
'''
'''
这个例子本质不是闭包值传递,而是正常的函数值传递
而由于每次都会创建一个局部变量,不同函数对应的局部变量的内存必然不同,因此不存在影响
'''
funcs = []
for x in range(7):
def some_func(x=x):
return x
funcs.append(some_func)
Output:
>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]
□in句の実行タイミング:ジェネレータ式では、宣言時にin句が実行され、実行時に条件句が実行されます。
array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
print(list(g))
"""
output:
[8]
"""
array_1 = [1,2,3,4]
g1 = (x for x in array_1)
array_1 = [1,2,3,4,5]
print(list(g1))
array_2 = [1,2,3,4]
g2 = (x for x in array_2)
array_2[:] = [1,2,3,4,5]
print(list(g2))
"""
output:
[1,2,3,4]
[1,2,3,4,5]
"""
□リストオブジェクトの乗算:リスト内の変数オブジェクトの場合、乗算を使用してコピーすると、コピーされたすべての変数オブジェクトは同じメモリ空間を指します
# 我们先初始化一个变量row
row = [""]*3 #row i['', '', '']
# 并创建一个变量board
board = [row]*3
>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]
□isでない:isは単一の2項演算子ではありません。これは、isを使用する場合とは異なり、個別には使用できません。演算子の両側の変数が同じオブジェクトを指している場合、結果はfalseではなく、それ以外の場合、結果はTrueになります。
□rで識別されるバックスラッシュ文字列:インタープリターは、バックスラッシュの動作を変更するだけなので、バックスラッシュと次の文字を直接渡します。このとき、文字列がバックスラッシュで終わる場合、エラーが報告されます。SyntaxError:文字列リテラルのスキャン中にEOL
□boolおよびint:ブール値はintのサブタイプであるため、Trueの整数値は1、Falseの整数値は0
□クラス属性とインスタンス属性:内部的には、クラス変数とインスタンス変数は、クラスオブジェクトのディクショナリ(つまり、__ dict__属性)を介して処理されます。現在のクラスのディクショナリに見つからない場合は、親クラスに移動して検索してください。+ =演算子は、新しいオブジェクトを作成するのではなく、変更可能なオブジェクトを変更します。したがって、この場合、1つのインスタンスのプロパティを変更すると、他のインスタンスとクラスのプロパティに影響します。
class A:
x = 1
class B(A):
pass
class C(A):
pass
Output:
>>> 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
(3, 2, 3)
>>> a = A()
>>> a.x, A.x
(3, 3)
>>> a.x += 1
>>> a.x, A.x
(4, 3)
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]
Output:
>>> 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
class Base:
inited = False
@classmethod
def set_inited(cls): # 实际可能传入Derived类
cls.inited = True # 并没有修改Base.inited,而是给Derived添加了成员
class Derived(Base):
pass
x = Derived()
x.set_inited()
if Base.inited:
print("Base is inited") # 不会被执行
□+ =演算子:+ =演算子は、リストを適切に変更します。要素の割り当て操作は機能しませんが、例外がスローされると、要素は適切に変更されます
'''
对于不可变对象, 这里指tuple, += 并不是原子操作, 而是 extend 和 = 两个动作
这里 = 操作虽然会抛出异常, 但 extend 操作已经修改成功了
'''
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) # 这里不出现错误
>>> 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])
□numpy.empty():numpy.empty()は、再初期化せずに直接次の空きメモリに戻ります。
import numpy as np
def energy_send(x):
# 初始化一个 numpy 数组
np.array([float(x)])
def energy_receive():
# 返回一个空的 numpy 数组
return np.empty((), dtype=np.float).tolist()
Output:
>>> energy_send(123.456)
>>> energy_receive()
123.456
□反復プロセスでの辞書の変更:結果は予測できないことが多いため、反復プロセス中に反復オブジェクトを変更しないようにしてください。
x = {0: None}
for i in x:
del x[i]
x[i+1] = None
print(i)
Output (Python 3.7):
# 这个结果好像跟字典的自动扩容有关,扩容会导致散列表地址发生变化而中断循环
0
1
2
3
4
□ジェネレータ式とリスト再帰変数スコープ:クラス定義にネストされたスコープは、クラス内の名前バインディングを無視します。ジェネレータ式には独自のスコープがあります。Python 3.X以降、リスト内包表記にも独自のスコープがあります。
# 这次我们先初始化x
x = -1
for x in range(7):
if x == 6:
print(x, ': for x inside loop')
print(x, ': x in global')
Output:
6 : for x inside loop
6 : x in global
x = 1
print([x for x in range(5)])
print(x, ': x in global')
Output (on Python 2.x):
[0, 1, 2, 3, 4]
(4, ': x in global')
# python 3 的列表推导式不再泄露
Output (on Python 3.x):
[0, 1, 2, 3, 4]
1 : x in global
x = 5
class SomeClass:
x = 17
y = (x for i in range(10))
Output:
>>> list(SomeClass.y)[0]
5
x = 5
class SomeClass:
x = 17
y = [x for i in range(10)]
Output (Python 2.x):
>>> SomeClass.y[0]
17
Output (Python 3.x):
>>> SomeClass.y[0]
5
□else:ループの後のelse句は、ループがbreakステートメントをトリガーせず、正常に終了した場合にのみ実行されます。tryステートメントの後のelse句に到達することは、tryブロックが実際に正常に完了したことを意味するため、tryの後のelse句は「完了句」とも呼ばれます。
def does_exists_num(l, to_find):
for num in l:
if num == to_find:
print("Exists!")
break
else:
print("Does not exist")
Output:
>>> some_list = [1, 2, 3, 4, 5]
>>> does_exists_num(some_list, 4)
Exists!
>>> does_exists_num(some_list, -1)
Does not exist
try:
pass
except:
print("Exception occurred!!!")
else:
print("Try block executed successfully...")
Output:
Try block executed successfully...
□ダブルアンダースコアのプライベート変数:Pythonでは、インタープリター は__
(ダブルアンダースコア)で始まり、最大で1つのアンダースコアで終わるクラスメンバー_NameOfTheClass
の名前を変更(マングル)します。
class Yo(object):
def __init__(self):
self.__honey = True
self.bitch = True
Output:
>>> Yo().bitch
True
>>> Yo().__honey
AttributeError: 'Yo' object has no attribute '__honey'
>>> Yo()._Yo__honey
True