Python 拾遗
哈
偶爾復(fù)習(xí),查漏補(bǔ)缺。(隨手寫的筆記,不建議看。。)
不可變對(duì)象
不可變對(duì)象常用在 參數(shù)共享/參數(shù)傳遞 上,好處很多,一是可以使用字符串池來節(jié)省空間,二是該對(duì)象可以安全地共享/傳遞,不會(huì)造成誤修改問題。
a. 問題
在使用*作為重復(fù)運(yùn)算符時(shí),如果目標(biāo)是一個(gè)嵌套的可變對(duì)象,就會(huì)產(chǎn)生令人費(fèi)解的問題:
>>> a = [1,2,3] >>> b = a * 3 >>> b [1, 2, 3, 1, 2, 3, 1, 2, 3] >>> b = [a] * 3 # nested >>> b [[1, 2, 3], [1, 2, 3], [1, 2, 3]] >>> b[1][1] = 4 >>> b [[1, 4, 3], [1, 4, 3], [1, 4, 3]]因?yàn)?* 并不是深拷貝,它只是簡單地復(fù)制了 [a] 這個(gè)列表,里面的 [1,2,3] 都是同一個(gè)對(duì)象,所以改了一個(gè),所有的都會(huì)改變。
解決方法是不要使用 * 號(hào),改用[a.copy() for i in range(3)] 執(zhí)行深拷貝。如果不需要修改,請(qǐng)直接使用不可變對(duì)象。
b. 奇技淫巧
作用域
不知是出于實(shí)現(xiàn)簡單或是性能,還是其他的原因,好像所有的語言都是這樣的。其實(shí)我更希望變量的作用域覆蓋會(huì)報(bào)錯(cuò)。
此外,還有一個(gè)小問題,先看一個(gè)例子:
>>> i = 4 >>> def f(): # 單純的從函數(shù)作用域訪問外部作用域是沒問題的 ... print(i) ... >>> f() 4再看一個(gè)問題舉例:
>>> i = 3 >>> def f(): ... print(i) # 這里應(yīng)該是訪問外部作用域 ... i = 5 # 可這里又定義了一個(gè)同名局部變量 i ... >>> f() # 于是就出錯(cuò)了 Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 2, in f UnboundLocalError: local variable 'i' referenced before assignment如果在內(nèi)部作用域先訪問外部作用域,再定義一個(gè)同名的局部變量,解釋器就懵逼了。
如果你其實(shí)想做的是改變?nèi)肿兞?i 的值,就應(yīng)該在開頭聲明 global i. 而如果 外部變量 i 不是存在于全局作用域,而是在某個(gè)閉合作用域內(nèi)的話,就該用 nonlocal i
inspect
P.S. 類比 Java 的反射,但是要簡單很多
函數(shù)式
生成器
Built-in Functions
Built-in Functions
時(shí)間
packages
__all__: 用于控制 import * 的行為。
通過字符串加載包:importlib
求值
compile
將 source 編譯成 AST 對(duì)象,該對(duì)象可傳給 eval 或 exec 執(zhí)行。
eval
只接受單個(gè)表達(dá)式 (expression),evaluate it,并返回表達(dá)式的值。
exec
接受包含語句 (statement) 的代碼段,execute it only for its side effects,無返回值。
裝飾器
裝飾器有兩種:用函數(shù)定義的裝飾器,還有用類定義的裝飾器。函數(shù)裝飾器最常用。
裝飾器可用于裝飾函數(shù),修改函數(shù)/類的某些行為,或者將函數(shù)注冊到別的地方。
1. 函數(shù)定義裝飾器
@decc def gg(xx):...# 等同于 def gg(xx) gg = decc(gg)帶參的裝飾器
@decorator(A, B) def F(arg):...F(99)# 等同于 def F(arg):...F = decorator(A, B)(F) # Rebind F to result of decorator's return value F(99) # Essentially calls decorator(A, B)(F)(99)上面演示的是用函數(shù)定義的裝飾器,也是最常用的裝飾器。
裝飾器接收的參數(shù)可以是各種各樣的,下面是一個(gè)帶參的裝飾器:
如果你的 on_command 有通用的部分,還可以將通用的部分抽離出來復(fù)用:
def _deco_maker(event_type: str) -> Callable: # 調(diào)用這個(gè),獲取 on_xxx 的 deco_deco,def deco_deco(self) -> Callable: # 這個(gè)對(duì)應(yīng) on_xxxdef deco(func: Callable) -> Callable: # 這個(gè)才是真正的裝飾器# do something return func # 返回原函數(shù)return decoreturn deco_deco我們知道 Python 的類實(shí)際上是可以很方便的修改的,因此函數(shù)裝飾器也能用于裝飾類,修改類的某些行為。
def log_getattribute(cls):# Get the original implementationorig_getattribute = cls.__getattribute__# Make a new definitiondef new_getattribute(self, name):print('getting:', name)return orig_getattribute(self, name)# Attach to the class and returncls.__getattribute__ = new_getattribute # 修改了被裝飾類 cls 的 __getattribute__return cls# Example use @log_getattribute class A:def __init__(self,x):self.x = xdef spam(self):pass2. 類定義裝飾器
類定義裝飾器和函數(shù)定義裝飾器的使用方式完全一致。它也可以用于裝飾函數(shù)或者類。
那么為啥還需要類定義裝飾器呢?它的優(yōu)勢在于類是可以繼承的,這樣的話,就能用繼承的方式定義裝飾器,將通用部分定義成超類。
類定義裝飾器的定義方法如下:
# PythonDecorators/entry_exit_class.py class entry_exit(object):def __init__(self, f):self.f = fdef __call__(self): #關(guān)鍵在于這個(gè)函數(shù),它使此類的對(duì)象變成 Callableprint("Entering", self.f.__name__)self.f()print("Exited", self.f.__name__)@entry_exit def func1():print("inside func1()")# 上面的裝飾器相當(dāng)于 func1 = entry_exit(func1) # 從這里看的話,裝飾器的行為完全一致# 接下來調(diào)用該函數(shù)(實(shí)際上是調(diào)用了 entry_exit 對(duì)象的 call 函數(shù)) func1()輸出結(jié)果如下:
Entering func1 inside func1() Exited func1OOP
- 直接通過超類名.__init__(self,xx)調(diào)用
- 通過super(__class__, self).__init__()調(diào)用。
(Python3 可直接用 super().__init__()
但是要搞清楚,super() 方法返回的是一個(gè)代理類。另外被代理的類也不一定是其超類。如果不清楚這些差別,最好還是顯式用方法一最好。)
@staticmethod @classmethod 與 Java 的 static 方法對(duì)比
python的類方法、靜態(tài)方法,與java的靜態(tài)方法:
java 中 constants、utils 這樣的靜態(tài)類,對(duì)應(yīng)的是python的一個(gè)模塊(文件),類屬性對(duì)應(yīng)模塊的全局屬性,靜態(tài)方法對(duì)應(yīng)模塊的函數(shù)
對(duì)于 java 中需要訪問類屬性的靜態(tài)方法,如果它不屬于第一類,應(yīng)該用 @classmethod 實(shí)現(xiàn)它。classmethod最大的特點(diǎn)就是一定有一個(gè) cls 傳入。這種方法的主要用途是實(shí)現(xiàn)工廠函數(shù)。
6.__getattr__: 攔截對(duì)不存在的屬性的訪問,可用于實(shí)現(xiàn)動(dòng)態(tài)分配屬性。
@property: 提供對(duì)屬性訪問的安全檢查
descriptor: get set delete 控制對(duì)類的訪問。(上面的 getattr 等是控制對(duì)類的屬性的訪問)
類構(gòu)造器 __new__:在 __init__ 之前運(yùn)行,它接收一個(gè) cls 參數(shù),然后使用它構(gòu)造并返回類實(shí)例 self。
類方法的 cls 即是當(dāng)前類,是 type 的實(shí)例,cls.xxx 和 <類名>.xxx 調(diào)用結(jié)果是一致的。而 self 由 __new__ 構(gòu)造,是 cls 的實(shí)例。
元類 metaclasses
元類,也就是用于創(chuàng)建class 的 class,算是很高級(jí)的話題了(If you wonder whether you need metaclasses, you don’t )
元類的工作流程:
詳細(xì)直接看 http://blog.jobbole.com/21351/ 吧。
查看源碼
Python 很多功能是 C 寫的,直接從 Pycharm 中只能看到自動(dòng)生成的文檔。
對(duì)一般的標(biāo)準(zhǔn)庫的模塊,查看方法是很簡單的:直接通過 __file__ 屬性就能看到。
而對(duì)于 builtins 模塊,都在這個(gè)文件里。
轉(zhuǎn)載于:https://www.cnblogs.com/kirito-c/p/9129456.html
總結(jié)
- 上一篇: 11组软件工程组队项目计划安排及项目介绍
- 下一篇: apache-jmeter-3.1的简单