Python プログラマーが知っておくべき、あまり知られていない 17 の質問

WBOY
リリース: 2016-06-16 08:44:00
オリジナル
987 人が閲覧しました

1. 可変オブジェクトを関数のデフォルト値として使用しない

コードをコピーします コードは次のとおりです:
in [1]:def append_to_list(value、def_list = []):
…:def_list.append(value)
…:
[2] 内: my_list = append_to_list(1)

イン [3]: my_list

アウト [3]: [1]


[4] 内: my_other_list = append_to_list(2)

In [5]: my_other_list

Out[5]: [1, 2] # 実際、当初は [2] のみを生成したかったのですが、最初の実行のエフェクト ページを取り込みました


[6] 内: インポート時間

[7] : def report_arg(my_default=time.time()):

...: print(my_default)

...:

[8] 内: report_arg() # 最初の実行

1399562371.32


[9] 内: time.sleep(2) # 2 秒後

[10]: report_arg()

1399562371.32 # 時刻は変更されていません


これら 2 つの例は、辞書、コレクション、リスト、その他のオブジェクトが関数のデフォルト値として適していないことを示しています。このデフォルト値は関数の作成時に生成されるため、各呼び出しではこのオブジェクトの「キャッシュ」が使用されます。以前に Python の高度なプログラミングを共有したときにもこの問題について言及しました。これは実際の開発で遭遇した問題です。学習したコードを確認してください。おそらく問題は明らかにされていません


次のように変更できます:


コードをコピーします コードは次のとおりです:def append_to_list(element, to=None):
to が None の場合:
to = []
to.append(element)
return to

2. ジェネレーターは反復後に結果を保持しません


コードをコピーします コードは次のとおりです: In [ 12]: gen = (i for i in range(5))
In [13]: 2 in genOut[13]: True


イン [14]: 3 世代

アウト [14]: True


In [15]: 1 in gen

Out[15]: False # 1 が gen にないのはなぜですか? 1->2 が呼び出されるため、この時点では 1 はイテレータに存在せず、 に生成されます。要求が通過しました


[20] 内: gen = (i for i in range(5))

[21] : a_list = list(gen) # リストに変換できます、もちろん a_tuple = tuple(gen) も使用できます

In [22]: 2 in a_list

Out[22]: True


In [23]: a_list の 3

Out[23]: True


In [24]: 1 in a_list # ループ後でも値はまだ存在します

Out[24]: True


3. Lambda はクロージャにローカル変数を保存します


コードをコピーします コードは次のとおりです: [29] 内: my_list = [lambda: i for i in range(5)]

In [30]: for l in my_list: ....: print(l())

....:
4
4
4
4
4

この問題の具体的な理由は、上記の Python の高度なプログラミングでまだ説明されています。実際、my_list に値を割り当てると、ラムダ式は i を実行し、i =4 になるまでループします。 、私は保持されます


ただし、ジェネレーター


を使用してコード をコピーできます。 コードは次のとおりです。 In [31] : my_gen = (ラムダ: n for n in range(5))

[32]: for l in my_gen: ....: print(l())

....:
0
1
2
3
4

リストに貼り付けることもできます:



コードをコピーします コードは次のとおりです:In [33]: my_list = [lambda x=i: x for i in range(5)] # 各ラムダ式にデフォルト値を割り当てていることを参照してください

[34]: for l in my_list: ....: print(l())

....:
0
1
2
3
4

ちょっとわかりにくいですよね。Python のもう 1 つの魔法を見てみましょう:



コードをコピーします コードは次のとおりです: In [35]: def groupby(items, size):
....: return zip(*[iter(items)] *サイズ)
.... :

In [36]: groupby(range(9), 3)Out[36]: [(0, 1, 2), (3, 4, 5), (6, 7, 8)]


グループ化された関数はわかりにくいですよね。ここで分析してみましょう



コードをコピーします コードは次のとおりです:

In [39]: [iter(items)]*3
Out[39]:
[,
 ,
 ] # 看到了吧, 其实就是把items变成可迭代的, 重复三回(同一个对象哦), 但是别忘了,每次都.next(), 所以起到了分组的作用
 In [40]: [lambda x=i: x for i in range(5)]
Out[40]:
[>,
 >,
 >,
 >,
 >] # 看懂了吗?


四、在循环中修改列表项

复制代码 代码如下:
In [44]: a = [1, 2, 3, 4, 5]

In [45]: for i in a:
   ....:     if not i % 2:
   ....:         a.remove(i)
   ....:

In [46]: a
Out[46]: [1, 3, 5] # 没有问题

In [50]: b = [2, 4, 5, 6]

In [51]: for i in b:
   ....:      if not i % 2:
   ....:          b.remove(i)
   ....:

In [52]: b
Out[52]: [4, 5] # 本来我想要的结果应该是去除偶数的列表


思考一下,为什么 – 是因为你对列表的remove,影响了它的index
复制代码 代码如下:

In [53]: b = [2, 4, 5, 6]

In [54]: for index, item in enumerate(b):
   ....:     print(index, item)
   ....:     if not item % 2:
   ....:         b.remove(item)
   ....:
(0, 2) # 这里没有问题 2被删除了
(1, 5) # 因为2被删除目前的列表是[4, 5, 6], 所以索引list[1]直接去找5, 忽略了4
(2, 6)

五、IndexError - 列表取值超出了他的索引数

复制代码 代码如下:

In [55]: my_list = [1, 2, 3, 4, 5]

In [56]: my_list[5] # 根本没有这个元素
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
in ()
----> 1 my_list[5]

IndexError: list index out of range # 抛异常了

In [57]: my_list[5:] # 但是可以这样, 一定要注意, 用好了是trick,用错了就是坑啊
Out[57]: []

六、重用全局变量

复制代码 代码如下:

In [58]: def my_func():
   ....:         print(var) # 我可以先调用一个未定义的变量
   ....:

In [59]: var = 'global' # 后赋值

In [60]: my_func() # 反正只要调用函数时候变量被定义了就可以了
global

In [61]: def my_func():
   ....:     var = 'locally changed'
   ....:

In [62]: var = 'global'

In [63]: my_func()

In [64]: print(var)

global # 局部变量没有影响到全局变量

In [65]: def my_func():
   ....:         print(var) # 虽然你全局设置这个变量, 但是局部变量有同名的, python以为你忘了定义本地变量了
   ....:         var = 'locally changed'
   ....:

In [66]: var = 'global'

In [67]: my_func()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
in ()
----> 1 my_func()

in my_func()
      1 def my_func():
----> 2         print(var)
      3         var = 'locally changed'
      4

UnboundLocalError: local variable 'var' referenced before assignment

In [68]: def my_func():
   ....:         global var # 这个时候得加全局了
   ....:         print(var) # 这样就能正常使用
   ....:         var = 'locally changed'
   ....:

In [69]: var = 'global'

In [70]:

In [70]: my_func()
global

In [71]: print(var)
locally changed # 但是使用了global就改变了全局变量

七、拷贝可变对象

复制代码 代码如下:
In [72]: my_list1 = [[1, 2, 3]] * 2

In [73]: my_list1
Out[73]: [[1, 2, 3], [1, 2, 3]]

In [74]: my_list1[1][0] = 'a' # 我只修改子列表中的一项

In [75]: my_list1
Out[75]: [['a', 2, 3], ['a', 2, 3]] # 但是都影响到了

In [76]: my_list2 = [[1, 2, 3] for i in range(2)] # 用这种循环生成不同对象的方法就不影响了

In [77]: my_list2[1][0] = 'a'

In [78]: my_list2
Out[78]: [[1, 2, 3], ['a', 2, 3]]


八、python多继承(C3)

复制代码 代码如下:

In [1]: class A(object):
   ...:         def foo(self):
   ...:                 print("class A")
   ...:

In [2]: class B(object):
   ...:         def foo(self):
   ...:                 print("class B")
   ...:

In [3]: class C(A, B):
   ...:         pass
   ...:

In [4]: C().foo()
class A # 例子很好懂, C继承了A和B,从左到右,发现A有foo方法,返回了

看起来都是很简单, 有次序的从底向上,从前向后找,找到就返回. 再看例子:

复制代码 代码如下:

In [5]: class A(object):
   ...:        def foo(self):
   ...:               print("class A")
   ...:

In [6]: class B(A):
   ...:        pass
   ...:

In [7]: class C(A):
   ...:        def foo(self):
   ...:               print("class C")
   ...:

In [8]: class D(B,C):
   ...:        pass
   ...:

In [9]: D().foo()
class C # ? 按道理, 顺序是 D->B->A,为什么找到了C哪去了


这也就涉及了MRO(Method Resolution Order):
复制代码 代码如下:

In [10]: D.__mro__
Out[10]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
简单的理解其实就是新式类是广度优先了, D->B, 但是发现C也是继承A,就先找C,最后再去找A

九、列表的+和+=, append和extend

复制代码 代码如下:
In [17]: print('ID:', id(a_list))
('ID:', 4481323592)

In [18]: a_list += [1]

In [19]: print('ID (+=):', id(a_list))
('ID (+=):', 4481323592) # 使用+= 还是在原来的列表上操作

In [20]: a_list = a_list + [2]

In [21]: print('ID (list = list + ...):', id(a_list))
('ID (list = list + ...):', 4481293056) # 简单的+其实已经改变了原有列表
In [28]: a_list = []

In [29]: id(a_list)
Out[29]: 4481326976

In [30]: a_list.append(1)

In [31]: id(a_list)
Out[31]: 4481326976 # append 是在原有列表添加

In [32]: a_list.extend([2])

In [33]: id(a_list)
Out[33]: 4481326976 # extend 也是在原有列表上添加

十、datetime也有布尔值
这是一个坑

复制代码 代码如下:

In [34]: import datetime

In [35]: print('"datetime.time(0,0,0)" (Midnight) ->', bool(datetime.time(0,0,0)))
('"datetime.time(0,0,0)" (Midnight) ->', False)

In [36]: print('"datetime.time(1,0,0)" (1 am) ->', bool(datetime.time(1,0,0)))
('"datetime.time(1,0,0)" (1 am) ->', True)


十一、'==' 和 is 的区别
我的理解是”is”是判断2个对象的身份, ==是判断2个对象的值

复制代码 代码如下:

In [37]: a = 1

In [38]: b = 1

In [39]: print('a is b', bool(a is b))
('a is b', True)

In [40]: c = 999

In [41]: d = 999

In [42]: print('c is d', bool(c is d))
('c is d', False) # 原因是python的内存管理,缓存了-5 - 256的对象

In [43]: print('256 is 257-1', 256 is 257-1)
('256 is 257-1', True)

In [44]: print('257 is 258-1', 257 is 258 - 1)
('257 is 258-1', False)

In [45]: print('-5 is -6+1', -5 is -6+1)
('-5 is -6+1', True)

In [46]: print('-7 is -6-1', -7 is -6-1)
('-7 is -6-1', False)
In [47]: a = 'hello world!'

In [48]: b = 'hello world!'

In [49]: print('a is b,', a is b)
('a is b,', False) # 很明显 他们没有被缓存,这是2个字段串的对象

In [50]: print('a == b,', a == b)
('a == b,', True) # 但他们的值相同
# But, 有个特例
In [51]: a = float('nan')

In [52]: print('a is a,', a is a)
('a is a,', True)

In [53]: print('a == a,', a == a)
('a == a,', False) # 亮瞎我眼睛了~


十二、浅拷贝和深拷贝
我们在实际开发中都可以向对某列表的对象做修改,但是可能不希望改动原来的列表. 浅拷贝只拷贝父对象,深拷贝还会拷贝对象的内部的子对象

复制代码 代码如下:

In [65]: list1 = [1, 2]

In [66]: list2 = list1 # 就是个引用, 你操作list2,其实list1的结果也会变

In [67]: list3 = list1[:]

In [69]: import copy

In [70]: list4 = copy.copy(list1) # 他和list3一样 都是浅拷贝

In [71]: id(list1), id(list2), id(list3), id(list4)
Out[71]: (4480620232, 4480620232, 4479667880, 4494894720)

In [72]: list2[0] = 3

In [73]: print('list1:', list1)
('list1:', [3, 2])

In [74]: list3[0] = 4

In [75]: list4[1] = 4

In [76]: print('list1:', list1)
('list1:', [3, 2]) # 对list3和list4操作都没有对list1有影响

# 再看看深拷贝和浅拷贝的区别

In [88]: from copy import copy, deepcopy

In [89]: list1 = [[1], [2]]

In [90]: list2 = copy(list1) # 还是浅拷贝

In [91]: list3 = deepcopy(list1) # 深拷贝

In [92]: id(list1), id(list2), id(list3)
Out[92]: (4494896592, 4495349160, 4494896088)

In [93]: list2[0][0] = 3

In [94]: print('list1:', list1)
('list1:', [[3], [2]]) # 看到了吧 假如你操作其子对象 还是和引用一样 影响了源

In [95]: list3[0][0] = 5

In [96]: print('list1:', list1)
('list1:', [[3], [2]]) # 深拷贝就不会影响

十三、bool其实是int的子类

复制代码 代码如下:

In [97]: isinstance(True, int)
Out[97]: True

In [98]: True + True
Out[98]: 2

In [99]: 3 * True + True
Out[99]: 4

In [100]: 3 * True - False
Out[100]: 3

In [104]: True << 10
Out[104]: 1024

十五、元组是不是真的不可变?

复制代码 代码如下:

In [111]: tup = ([],)

In [112]: tup[0] += [1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 tup[0] += [1]

TypeError: 'tuple' object does not support item assignment

In [113]: tup
Out[113]: ([1],) # 我靠 又是亮瞎我眼睛,明明抛了异常 还能修改?

In [114]: tup = ([],)

In [115]: tup[0].extend([1])

In [116]: tup[0]
Out[116]: [1] # 好吧,我有点看明白了, 虽然我不能直接操作元组,但是不能阻止我操作元组中可变的子对象(list)

これは Python の += は奇妙です、パート II のわかりやすい説明です:

コードをコピーします コードは次のとおりです:

[117] 内: my_tup = (1,)

[118] 内: my_tup += (4,)

[119] 内: my_tup = my_tup + (5,)

In [120]: my_tup
Out[120]: (1, 4, 5) # ? そうですね、タプルは操作できませんね?

[121] 内: my_tup = (1,)

[122] 内: print(id(my_tup))
4481317904

[123] 内: my_tup += (4,)

In [124]: print(id(my_tup))
4480606864 # この操作は元のタプルではないため、

となります。

[125] 内: my_tup = my_tup + (5,)

[126] 内: print(id(my_tup))
4474234912

16. Python にはプライベート メソッド/変数はありませんが、「疑似」を持つことができます。

コードは次のとおりです。以下のように:In [127]: class my_class(object^E):
.....: def public_method(self):
.....: print ('Hello public world!')
.....: def __private_method(self): # Private は二重アンダースコアで始まります
....: print('Hello private world!')
.... .: def call_private_method_in_class(self):
.....: self.__private_method()

[132] 内: my_instance = my_class()

[133] 内: my_instance.public_method()

こんにちは、パブリック ワールドです! # 通常のメソッド


[134]: my_instance._my_class__private_method()

Hello private world! # プライベートの場合は、「_ + クラス名 + プライベート メソッド名」を追加できます


[135]: my_instance.call_private_method_in_class()

Hello private world! # クラスによって提供されるパブリック インターフェイスを通じて内部的にアクセスすることもできます


イン [136]: my_instance._my_class__private_variable

アウト [136]: 1


17. 例外処理と else


コードをコピーします コードは次のとおりです: [ 150] 内: try:
.....: print('第三の要素:', a_list[2])
.....: IndexError:
を除く .....: print( 'raised IndexError')
.....: else:
.....: print('no error in try-block') # Else は例外がない場合にのみ実行されます。 try Expression
.....:
raised IndexError # 例外がスローされ、完全に完了しませんでした
In [153]: i = 0

[154]: while i .....: print(i)

.....: i += 1
.....: else :
.....: print('in else')
.....:
0
1
in else # while もサポートされています~
In [ 155]: i = 0

[156]: while i .....: print(i)

.....: i += 1
....: ブレーク
.....: else:
.....: print('completed while-loop')
.....:
0 # 壊れていて完全には実行されませんでしたelse の
は実行されません。 In [158]: for i in range(2):
.....: print(i)
.....: else:
.....: PRINT ('完了した for ループ')
.....:
0
1
完了した for-lo
In [159]: for i in range(2):
.....: print(i)

.....: Break

....: else:
.....: print('completed for-loop')
.....:
0 # これもブレークのため

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート