制作ip池的時候,可能需要不定時新代理池獲取的網(wǎng)站,通常的做法是每次添加一個新的網(wǎng)站,就改一下獲取函數(shù),這樣更改添加方法很容易出錯.我們可以設(shè)想另外一種模式,當(dāng)我們要添加新的獲取網(wǎng)站的時候,我們只要添加對應(yīng)的函數(shù)就可以了,而不需要更改其他代碼,這樣會方便很多,這里我們借助元類來實(shí)現(xiàn).
__metaclass__ 的一些理解
以下是我個人理解:
- python中,一切皆對象,元類也是一個對象
- 元類是可以創(chuàng)造類對象的一種類
- type除了顯示對象類型以外,還可以作為關(guān)鍵字去創(chuàng)建類
參考別人的資料得到的信息:
-
類也是對象,當(dāng)使用關(guān)鍵字class的時候python解釋器就會自動的取創(chuàng)建一個對象,這個對象自身擁有創(chuàng)建對象(類實(shí)例,也就是我們說的類實(shí)例化出來的對象)的能力,它的本質(zhì)仍然是一個對象,于是你可以對它進(jìn)行如下操作:
- 你可以把它復(fù)制給一個變量
- 你可以copy它
- 你可以為它增加屬性
- 你可以將他作為參數(shù)進(jìn)行傳遞
可以動態(tài)的在方法中創(chuàng)建類,但是仍然需要你自己編寫整個類的方法和屬性,如下列子:
def choose_class(name):
if name == 'foo':
class Foo(object): # 這里就是動態(tài)的根據(jù)條件來創(chuàng)建類但是關(guān)于它的方法仍然需要自己填寫完整才能繼續(xù)
pass
return Foo # 返回的是類,不是類的實(shí)例
else:
class Bar(object):
pass
return Bar
除了手動創(chuàng)建類以外typey可以可以創(chuàng)建類的,格式如下:
type(類名,父類的元祖(在有繼承的情況下,可以為空),包含屬性的字典(名稱和值))
比如我們要創(chuàng)建一個名為TestFunc(object)的類,它有一個屬性叫做name 值是 TestFunc, 那么我們可以按照下面的方式創(chuàng)建:
type(TestFunc,(object),{'name':'TestFunc'})
這樣我們就使用表達(dá)式創(chuàng)建了一個類,這個類和上面的模式使用class創(chuàng)建的類是沒有區(qū)別的.在這里發(fā)現(xiàn)可以批量性的根據(jù)自己的需要來創(chuàng)建類了.
__metaclass__屬性
這是一個類屬性,當(dāng)你用這個屬性創(chuàng)建類的時候,它就會用元類來創(chuàng)建類.
class TestFunc(AClass):
__metaclass__=someting
如果你這么做了桦踊,Python就會用元類來創(chuàng)建類Foo伶跷。這里面可以這樣理解枪芒。首先寫下class Foo(object),但是類對象Foo還沒有在內(nèi)存中創(chuàng)建猪狈。Python會在類的定義中尋找__metaclass__屬性抛蚁,如果找到了陈醒,Python就會用它來創(chuàng)建類Foo,如果沒有找到瞧甩,就會用內(nèi)建的type來創(chuàng)建這個類钉跷。把下面這段話反復(fù)讀幾次。當(dāng)你寫如下代碼時:
class TestFunc(Aclass):
pass
Python做了如下的操作:
Foo中有__metaclass__這個屬性嗎肚逸?如果是爷辙,Python會在內(nèi)存中通過__metaclass__創(chuàng)建一個名字為Foo的類對象(我說的是類對象,請緊跟我的思路)朦促。如果Python沒有找到__metaclass__膝晾,它會繼續(xù)在Bar(父類)中尋找__metaclass__屬性,并嘗試做和前面同樣的操作务冕。如果Python在任何父類中都找不到__metaclass__血当,它就會在模塊層次中去尋找__metaclass__,并嘗試做同樣的操作禀忆。如果還是找不到__metaclass__,Python就會用內(nèi)置的type來創(chuàng)建這個類對象臊旭。
你可以在metaclass中放置些什么代碼呢?答案就是:可以創(chuàng)建一個類的東西箩退。那么什么可以用來創(chuàng)建一個類呢离熏?type,或者任何使用到type或者子類化type的都可以戴涝。
下面就回歸到了我們開篇說的問題如何子在不修改類已經(jīng)存在的方法的基礎(chǔ)上擴(kuò)展我們的類,答案是 -- 自定義元類
我們獲取類中所有以newfunc開頭的函數(shù)
class NewMetaclass(type):
def __new__(cls, name, bases, attrs):
count = 0
attrs['__NewFunc__'] = []
for k, v in attrs.items():
if 'newfunc' in k:
attrs['__NewFunc__'].append(k)
count += 1
attrs['__NewFuncCount__'] = count
return type.__new__(cls, name, bases, attrs)
class NewFunc(object, metaclass=NewMetaclass):
def newfunc_a(self):
return 'a1'
def new_a(self):
return 'a2'
def newfunc_b(self):
return 'b1'
def new_b(self):
return 'b2'
test = NewFunc
count = test.__NewFuncCount__
content = test.__NewFunc__
print(count, content)
'''
運(yùn)行結(jié)果如下:
2 ['newfunc_a', 'newfunc_b']
'''
這里我們解釋下幾個參數(shù),這里對應(yīng)著我們上面說的使用type創(chuàng)建類的參數(shù):
type(類名,父類的元祖(在有繼承的情況下需要寫出來,其他可以為空),包含屬性的字典(名稱和值))
參數(shù) | 含義 |
---|---|
cls | 類的實(shí)例對象 |
name | 類的名字 |
bases | 類所有繼承的類 |
attrs | 包含屬性的字典 |
我們?yōu)槭裁匆褂迷?引用Tim Peters的一句話:
“元類就是深度的魔法撤奸,99%的用戶應(yīng)該根本不必為此操心。如果你想搞清楚究竟是否需要用到元類喊括,那么你就不需要它胧瓜。那些實(shí)際用到元類的人都非常清楚地知道他們需要做什么,而且根本不需要解釋為什么要用元類郑什「” —— Python界的領(lǐng)袖 Tim Peters
其實(shí)我們使用元類更多的就是為了創(chuàng)建API,讓功能更加方便
參考文章: 深度理解python中的元類
eval() 函數(shù)的使用
eval()官方文檔里面給出來的功能解釋是:將字符串string對象轉(zhuǎn)化為有效的表達(dá)式參與求值運(yùn)算返回計(jì)算結(jié)果
語法上:eval(expression,globals=None, locals=None)
返回的是計(jì)算結(jié)果
參數(shù)的含義:
- expression是一個參與計(jì)算的python表達(dá)式
- globals是可選的參數(shù)蘑拯,如果設(shè)置屬性不為None的話钝满,就必須是dictionary對象了
- locals也是一個可選的對象,如果設(shè)置屬性不為None的話申窘,可以是任何map對象了
python是用命名空間來記錄變量的軌跡的弯蚜,命名空間是一個dictionary,鍵是變量名剃法,值是變量值碎捺。
當(dāng)一行代碼要使用變量 x 的值時,Python 會到所有可用的名字空間去查找變量,按照如下順序:
1)局部名字空間 - 特指當(dāng)前函數(shù)或類的方法收厨。如果函數(shù)定義了一個局部變量 x, 或一個參數(shù) x晋柱,Python 將使用它,然后停止搜索诵叁。
2)全局名字空間 - 特指當(dāng)前的模塊雁竞。如果模塊定義了一個名為 x 的變量,函數(shù)或類拧额,Python 將使用它然后停止搜索碑诉。
3)內(nèi)置名字空間 - 對每個模塊都是全局的。作為最后的嘗試侥锦,Python 將假設(shè) x 是內(nèi)置函數(shù)或變量联贩。
python的全局名字空間存儲在一個叫g(shù)lobals()的dict對象中;局部名字空間存儲在一個叫l(wèi)ocals()的dict對象中捎拯。我們可以用print (locals())來查看該函數(shù)體內(nèi)的所有變量名和變量值泪幌。
也就是說我們給它傳入一個字符串,它會取執(zhí)行這個字符串所代表的函數(shù)
demo:
def change():
print('this is change func')
funcname = '{}()'.format('change')
print(funcname)
eval(funcname)
'''
結(jié)果如下:
change()
this is change func
'''
當(dāng)然這個函數(shù)使用起來也有很大的風(fēng)險,這里不再詳細(xì)說明給幾個網(wǎng)址大家可以研究下: