1 什么是反射?以及應用場景?
test.py
def f1():print('f1')
def f2():print('f2')
def f3():print('f3')
def f4():print('f4')
a = 1
復制代碼
import test as ss
ss.f1()
ss.f2()
print(ss.a)
復制代碼
我們要導入另外一個模塊,可以使用import.現在有這樣的需求,我動態輸入一個模塊名,可以隨時訪問到導入模塊中的方法或者變量,怎么做呢?
imp = input(“請輸入你想導入的模塊名:”)
CC = __import__(imp) 這種方式就是通過輸入字符串導入你所想導入的模塊
CC.f1() # 執行模塊中的f1方法#或者用importlib模塊
import importlib
importlib.import_module(name="",package="")
復制代碼
上面我們實現了動態輸入模塊名,從而使我們能夠輸入模塊名并且執行里面的函數。但是上面有一個缺點,那就是執行的函數被固定了。那么,我們能不能改進一下,動態輸入函數名,并且來執行呢?
#dynamic.py
imp = input("請輸入模塊:")
dd = __import__(imp)
# 等價于import imp
inp_func = input("請輸入要執行的函數:")
f = getattr(dd,inp_func,None)#作用:從導入模塊中找到你需要調用的函數inp_func,然后返回一個該函數的引用.沒有找到就煩會None
f() # 執行該函數
復制代碼
反射機制 上面說了那么多,到底什么是反射機制呢?
??其實,反射就是通過字符串的形式,導入模塊;通過字符串的形式,去模塊尋找指定函數,并執行。利用字符串的形式去對象(模塊)中操作(查找/獲取/刪除/添加)成員,一種基于字符串的事件驅動!
- getattr()函數是Python自省的核心函數,具體使用大體如下:
class A:
def __init__(self): self.name = 'zhangjing'#self.age='24'
def method(self): print"method print"
Instance = A()
print getattr(Instance , 'name, 'not find') #如果Instance 對象中有屬性
name則打印self.name的值,否則打印'not find'
print getattr(Instance , 'age', 'not find') #如果Instance 對象中有屬性
age則打印self.age的值,否則打印'not find'
print getattr(a, 'method', 'default') #如果有方法method,否則打印其地址,
否則打印default
print getattr(a, 'method', 'default')() #如果有方法method,運行函數并
打印None否則打印default
復制代碼
- hasattr(object, name)
說明:判斷對象object是否包含名為name的特性(hasattr是通過調用getattr(ojbect, name)
是否拋出異常來實現的)
復制代碼
- setattr(object, name, value)
這是相對應的getattr()。參數是一個對象,一個字符串和一個任意值。字符串可能
會列出一個現有的屬性或一個新的屬性。這個函數將值賦給屬性的。該對象允許它提供
。例如,setattr(x,“foobar”,123)相當于x.foobar = 123。
復制代碼
- delattr(object, name)
與setattr()相關的一組函數。參數是由一個對象(記住python中一切皆是對象)和一
個字符串組成的。string參數必須是對象屬性名之一。該函數刪除該obj的一個由string
指定的屬性。delattr(x, 'foobar')=del x.foobar我們可以利用上述的四個函數,來對模塊進行一系列操作.r = hasattr(commons,xxx)判斷某個函數或者變量是否存在
print(r)
setattr(commons,'age',18) 給commons模塊增加一個全局變量age = 18,創建成功返回none
setattr(config,'age',lambda a:a+1) //給模塊添加一個函數
delattr(commons,'age')//刪除模塊中某個變量或者函數
注:getattr,hasattr,setattr,delattr對模塊的修改都在內存中進行,并不會影響文件中真實內容。
復制代碼
2 metaclass作用?以及應用場景?
元類(metaclass)的理解和簡單運用 首先這里討論的python類,都基于繼承于object的新式類進行討論。
首先在python中,所有東西都是對象。這句話非常重要要理解元類我要重新來理解一下python中的類
class Trick(object):pass
復制代碼
當python在執行帶class語句的時候,會初始化一個類對象放在內存里面。例如這里會初始化一個Trick對象
這個對象(類)自身擁有創建對象(通常我們說的實例,但是在python中還是對象)的能力。
為了方便后續理解,我們可以先嘗試一下在新式類中最古老厲害的關鍵字type。
input:
class Trick(object):pass
print type('123')
print type(123)
print type(Trick)
output:
<type 'str'>
<type 'int'>
<class '__main__.Trick'>
復制代碼
可以看到能得到我們平時使用的 str, int, 以及我們初始化的一個實例對象Trick()
但是下面的方法你可能沒有見過,type同樣可以用來動態創建一個類
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
這個怎么用呢,我要用這個方法創建一個類 讓我們看下下面的代碼
input:
print type('Trick', (), {})output:
<class '__main__.Trick'>
同樣我們可以實例化這個類對象input:
print type('trick', (), {})()output:
<__main__.trick object at 0x109283450>
可以看到,這里就是一個trick的實例對象了。
復制代碼
同樣的這個方法還可以初始化創建類的父類,同時也可以初始化類屬性:
input:
class FlyToSky(object):passpw = type('Trick', (FlyToSky, ), {'laugh_at': 'hahahaha'})
print pw().laugh_at
print pw.__dict__
print pw.__bases__
print pw().__class__
print pw().__class__.__class__output:
hahahaha
{'__module__': '__main__', 'laugh_at': 'hahahaha', '__doc__': None}
(<class '__main__.FlyToSky'>,)
<class '__main__.Trick'>
<type 'type'>
復制代碼
下面我將依次理一下上面的內容,在此之前我必須先介紹兩個魔法方法:
__class__這個方法用于查看對象屬于是哪個生成的,這樣理解在python中的所有東西都是對象,類對象也是對象。如果按照以前的思維來想的話就是類是元類的實例,而實例對象是類的實例。__bases__這個方法用于得到一個對象的父類是誰,特別注意一下__base__返回單個父類,__bases__以tuple形式返回所有父類。
復制代碼
好了知道了這兩個方法我來依次說一下每行什么意思。
使用type創建一個類賦值給pw type的接受的三個參數的意思分辨是(類的名稱, 類是否有父類(), 類的屬性字典{})
這里初始化一個類的實例,然后嘗試去獲得類屬性 的laugh_at 屬性值,然后得到結果hahahaha
取一個pw的也就是我們常見類的類字典數據
拿到pw的父類,結果是我們指定的 FlyToSky
pw的實例pw()屬于哪個類初始化的,可以看到是class Trick
我們再看class trick是誰初始化的? 就是元類type了
什么是元類以及簡單運用
這一切介紹完之后我們總算可以進入正題
到底什么是元類?通俗的就是說,元類就是創建類的類。。。這樣聽起來是不是超級抽象?
來看看這個
Trick = MetaClass()
MyObject = Trick()
復制代碼
上面我們已經介紹了,搞一個Trick可以直接這樣
Trick = type('Trick', (), {}) 可以這樣其實就是因為,Type實際上是一個元類,用他可以去創建類。什么是元類剛才說了,元類就是創建類的類。也可以說他就是一個類的創建工廠。
類上面的__metaclass__屬性,相信愿意了解元類細節的盆友,都肯定見過這個東西,而且為之好奇。不然我不知道是什么支撐你看到這里的?。使用了__metaclass__這個魔法方法就意味著就會用__metaclass__指定的元類來創建類了。
class Trick(FlyToSky):pass
復制代碼
當我們在創建上面的類的時候,python做了如下的操作:
Trick中有__metaclass__這個屬性嗎?如果有,那么Python會在內存中通過__metaclass__創建一個名字為Trick的類對象,也就是Trick這個東西。如果Python沒有找到__metaclass__,它會繼續在自己的父類FlyToSky中尋找__metaclass__屬性,并且嘗試以__metaclass__指定的方法創建一個Trick類對象。如果Python在任何一個父類中都找不到__metaclass__,它也不會就此放棄,而是去模塊中搜尋是否有__metaclass__的指定。如果還是找不到,好吧那就是使用默認的type來創建Trick。
那么問題來了,我們要在__metaclass__中放置什么呢?答案是可以創建一個類的東西,type,或者任何用到type或子類化type的東西都行。
自定義元類
自定義類的的目的,我總結了一下就是攔截類的創建,然后修改一些特性,然后返回該類。是不是有點熟悉?沒錯,就是感覺是裝飾器干的事情,只是裝飾器是修飾一個函數,同樣是一個東西進去,然后被額外加了一些東西,最后被返回。
其實除了上面談到的制定一個__metaclass__并不需要賦值給它的不一定要是正式類,是一個函數也可以。要創建一個使所有模塊級別都是用這個元類創建類的話,在模塊級別設定__metaclass__就可以了。先寫一個來試試看
class UpperAttrMetaClass(type):def __new__(mcs, class_name, class_parents, class_attr):attrs = ((name, value) for name, value in class_attr.items() if not name.startswith('__'))uppercase_attrs = dict((name.upper(), value) for name, value in attrs)return super(UpperAttrMetaClass, mcs).__new__(mcs, class_name, class_parents, uppercase_attrs)class Trick(object):__metaclass__ = UpperAttrMetaClassbar = 12money = 'unlimited'print(Trick.bar)
print(Trick.money)
復制代碼
3 用盡量多的方法實現單例模式。
單例模式 單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。
在 Python 中,我們可以用多種方法來實現單例模式
實現單例模式的幾種方式 使用模塊 其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成 .pyc 文件,當第二次導入時,就會直接加載 .pyc 文件,而不會再次執行模塊代碼。因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:
class Singleton(object):def foo(self):pass
singleton = Singleton()
復制代碼
將上面的代碼保存在文件 mysingleton.py 中,要使用時,直接在其他文件中導入此文件中的對象,這個對象即是單例模式的對象
from a import singleton
使用裝飾器
def Singleton(cls):_instance = {}def _singleton(*args, **kargs):if cls not in _instance:_instance[cls] = cls(*args, **kargs)return _instance[cls]return _singleton@Singleton
class A(object):a = 1def __init__(self, x=0):self.x = xa1 = A(2)
a2 = A(3)
復制代碼
使用類
class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance
復制代碼
一般情況,大家以為這樣就完成了單例模式,但是這樣當使用多線程時會存在問題
class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance
import threading
def task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
程序執行后,打印結果如下:<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
復制代碼
看起來也沒有問題,那是因為執行速度過快,如果在init方法中有一些IO操作,就會發現問題了,下面我們通過time.sleep模擬
我們在上面__init__方法中加入以下代碼:
def __init__(self):import timetime.sleep(1)
復制代碼
重新執行程序后,結果如下
<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
復制代碼
問題出現了!按照以上方式創建的單例,無法支持多線程
解決辦法:加鎖!未加鎖部分并發執行,加鎖部分串行執行,速度降低,但是保證了數據安全
import time
import threading
class Singleton(object):_instance_lock = threading.Lock()def __init__(self):time.sleep(1)@classmethoddef instance(cls, *args, **kwargs):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)
for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
打印結果如下:<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
復制代碼
這樣就差不多了,但是還是有一點小問題,就是當程序執行時,執行了time.sleep(20)后,下面實例化對象時,此時已經是單例模式了,但我們還是加了鎖,這樣不太好,再進行一些優化,把intance方法,改成下面的這樣就行:
@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance
復制代碼
這種方式實現的單例模式,使用時會有限制,以后實例化必須通過 obj = Singleton.instance()
如果用 obj=Singleton() ,這種方式得到的不是單例
基于__new__方法實現(推薦使用,方便) 通過上面例子,我們可以知道,當我們實現單例時,為了保證線程安全需要在內部加入鎖
我們知道,當我們實例化一個對象時,是先執行了類的__new__方法(我們沒寫時,默認調用object.new),實例化對象;然后再執行類的__init__方法,對這個對象進行初始化,所有我們可以基于這個,實現單例模式
import threading
class Singleton(object):_instance_lock = threading.Lock()def __init__(self):passdef __new__(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = object.__new__(cls) return Singleton._instanceobj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)def task(arg):obj = Singleton()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
復制代碼
采用這種方式的單例模式,以后實例化對象時,和平時實例化對象的方法一樣 obj = Singleton()
基于metaclass方式實現 """ 1.類由type創建,創建類時,type的__init__方法自動執行,類() 執行type的 __call__方法(類的__new__方法,類的__init__方法) 2.對象由類創建,創建對象時,類的__init__方法自動執行,對象()執行類的 call 方法 """
import threading
class SingletonType(type):_instance_lock = threading.Lock()def __call__(cls, *args, **kwargs):if not hasattr(cls, "_instance"):with SingletonType._instance_lock:if not hasattr(cls, "_instance"):cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)return cls._instanceclass Foo(metaclass=SingletonType):def __init__(self,name):self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
復制代碼