1. 程式人生 > >大發彩_票平臺搭建

大發彩_票平臺搭建

manager 內容 其實在 裝飾器。 裝飾 理解 after 自省 內部函數

大發彩_票平臺搭建
地址一:【hubawl.com】狐霸源碼論壇
地址二:【bbscherry.com】

內建函數(xxx)

背景:為什麽要有這種帶下劃線的內建函數,個人認為這種內建函數開放了很多Python的特殊用法,只要詳盡掌握,就會理解平時用到的數據結構是復寫了什麽方法,自己也可以寫出類似set,dqueue,dict,list的數據類型方法。

1.1 類似字典的操作方式,__getitem__,__setitem__,__delitem__

    我們熟悉的字典是可以像如下方式操作賦值取值:

d = dict()
d["a"] = 1
d["b"] = 2

print d
del d["a"]
print d
其實像這種賦值方式就是復寫了setitem,getitem,delitem請見如下類

class TestDict(object):

def getitem(self,key):
return self.dict.get(key)

def setitem(self,key,value):
self.dict[key] = value

def delitem(self,key):
self.dict.pop(key)

td = TestDict()
td["a"] = 1
td["b"] = 2
print td["a"]

print td.dict
del td["a"]
print td.dict
為了更加靈活,如上代碼也可以在對象執行賦值方法(init)的時候聲明個變量d = dict(),然後對這個變量進行賦值和取值操作也可以模擬上面的操作。

1.2 __new__和__init__

    __new__: 在類實例化的時候調用,用來創建實例,如果不返回實例那麽__init__將不會執行,第一個參數是class對象,在創建實例的時候需要有返回值

    __init__: 在初始化實例的時候調用,比如說實例屬性賦值,第一個參數是實例對象,一般都重寫__init__方法,在執行的時候不需要返回值

class TestNew(object):
def new(cls, *args, *kwargs):
print ‘new called.‘
return super(TestNew,cls).new(cls,
args,**kwargs)
def init(self):
print ‘init called.‘
self.a = 1

tn = TestNew()
print tn.a
可以用new來實現單例模式

class Singleton(object):
def new(cls):

關鍵在於這,每一次實例化的時候,我們都只會返回這同一個instance對象

        if not hasattr(cls, ‘instance‘):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance

obj1 = Singleton()
obj2 = Singleton()

obj1.attr1 = ‘value1‘
print obj1.attr1, obj2.attr1
print obj1 is obj2
Tips: 單例模式有很多種實現方式,也可以通過類變量+靜態方法的方式實現。

可以通過重載__new__來實現很多創建實例時的功能。

1.3 __iter__,next(python2),__next__(python3)

                  __iter__: 復寫這個函數的對象是可叠代對象

        next/__next__: 復寫這個函數的對象都是一個叠代器

class TestIterNext(object):
def init(self,data=1):
self.data = data

def next(self):
    if self.data > 5:
        raise StopIteration
    else:
        self.data+=1
        return self.data

def __iter__(self):
    print "iter"
    return self

當for循環去叠代tin = TestIterNext()對象的時候第一步會去看iter是否返回一個生成器(generator),如果返回的是對象本身才會去執行next函數。
1.4 call

        把一個類實例的對象當做函數一樣調用就是復寫了__call__方法,如下:

class TestCall(object):
def call(self):
print "call it"
tc = TestCall()
tc()
復寫call方法大多數用在裝飾器類中(第5章)和繼承Type類(第3章)的時候。
1.5 repr,str

    __repr__和__str__沒有太大區別,在Python交互模式下才能發現。復寫改方法後輸出對象就是按照該方法裏的內容進行輸出。在類裏可以這樣用__repr__ = __str__

1.6 __all__

    可用於模塊導入時限制,,當我們from module import *的時候這個__all__就起作用了,__all__=["bar","sar"] ,[]裏定義函數或者變量類等,有些模塊內部一些函數不對外開放,此時把一些對外開放的函數變量放入到__all__裏就可以了,這樣避免了一些多余的導入。如果在__init__.py裏定義則在導入模塊的時候只導入__all__裏定義的各個文件,此時無法定義到文件裏具體哪個類或方法,如果需要細化則需要在具體的類裏寫入__all__。如果是from module import Test這種使用不受__all__限制

1.7 __setattr__,__getattr__,__delattr__

        __setattr__: 對變量賦值時調用。

        __getattr__:默認查找對象屬性是在一個字典裏(__dict__),這裏沒有要查找的對象則去__getattr__方法裏查找,如果我們復寫__getattr__則可以根據實際需求來返回值。

        __delattr__:刪除屬性時調用。

class TestAttr(object):
def init(self):
self.name = "abc"

def __getattr__(self, item):
    print "item:" + str(item)
    print "getattr"
    return 10
def __setattr__(self, *args, **kwargs):
    print "set attr"
    object.__setattr__(self,*args,**kwargs)

def __delattr__(self, *args, **kwargs):
    print "delete attr"
    object.__delattr__(self,*args, **kwargs)

ta = TestAttr()
print ta.dict
print ta.names
del ta.name
print ta.dict

1.8 __le__,__lt__,__ge__,__gt__,__ne__,__eq__

       一個對象和另一個對象比較大小,返回的並不一定是True和False,返回值有可能是我們定義的任何值,這裏就是復寫上面這些方法。如下:

class TestCompare(object):
def lt(self, other):
return "aaa"

t = TestCompare()
print t<1
這裏打印出的就不是True和False,打印的是我們再復寫的方法裏定義的"aaa",其實有一些常用的orm,比如說sqlalchemy裏查詢條件可以這樣寫

g.pg_db.query(Company.id).filter(Company.level_id == level)
這裏的Company.level_id == level返回的就是篩選條件,因為對Company的level_id對象復寫了eq

1.9 __slots__

    優點:

        1,更快的屬性訪問速度

        2,減少內存消耗

    每個類裏都維護一個字典__dict__,這個字典維護了對象的所有屬性,但如果成千上萬個對象則就會創建很多個__dict__來存放對象屬性,為了性能我們可以不用Python幫我們維護這個字典。此時我們在類裏定義__slots__ = ["name","age"]的時候就表示禁用了__dict__,並限定name和age為類的屬性(類裏只能有name和age屬性),這樣做的好處能大大節省內存開支,對象越多節省的就越多,大概能節省40%以上。

class TestSlots(object):
slots = ["name","age"]
def init(self, name, age):
self.name = name
self.age = age

ts = TestSlots("a",1)
ts.name = 1
print ts.name
2.0 metaclass

    在理解元類之前,你需要先掌握Python中的類。Python中類的概念借鑒於Smalltalk,這顯得有些奇特。在大多數編程語言中,類就是一組用來描述如何生成一個對象的代碼段。在Python中這一點仍然成立

    元類是什麽?

        但就元類本身而言,它們其實是很簡單的:
        1)   攔截類的創建
        2)   修改類
        3)   返回修改之後的類

    但是,Python中的類還遠不止如此。類同樣也是一種對象。是的,沒錯,就是對象。只要你使用關鍵字class,Python解釋器在執行的時候就會創建一個對象。下面的代碼段:

class ObjectCreator(object):
pass
將在內存中創建一個對象,名字就是ObjectCreator。這個對象(類)自身擁有創建對象(類實例)的能力,而這就是為什麽它是一個類的原因。但是,它的本質仍然是一個對象,於是乎你可以對它做如下的操作:

1) 你可以將它賦值給一個變量
2) 你可以拷貝它
3) 你可以為它增加屬性
4) 你可以將它作為函數參數進行傳遞
下面是示例:

print ObjectCreator

你可以打印一個類,因為它其實也是一個對象

def echo(o):
print o

echo(ObjectCreator)

你可以將類做為參數傳給函數

print hasattr(ObjectCreator, ‘new_attribute‘)
ObjectCreator.new_attribute = ‘foo‘

你可以為類增加屬性

print hasattr(ObjectCreator, ‘new_attribute‘)
print ObjectCreator.new_attribute
ObjectCreatorMirror = ObjectCreator

你可以將類賦值給一個變量

print ObjectCreatorMirror()
動態地創建類

因為類也是對象,你可以在運行時動態的創建它們,就像其他任何對象一樣。首先,你可以在函數中創建類,使用class關鍵字即可。

def choose_class(name):
if name == ‘foo‘:
class Foo(object):
pass
return Foo

返回的是類,不是類的實例

else:
    class Bar(object):
        pass
    return Bar

MyClass = choose_class(‘foo‘)
print MyClass

函數返回的是類,不是類的實例>>> print MyClass()

你可以通過這個類創建類實例,也就是對象

<main.foo object="" at="" 0x89c6d4c="">

但這還不夠動態,因為你仍然需要自己編寫整個類的代碼。由於類也是對象,所以它們必須是通過什麽東西來生成的才對。當你使用class關鍵字時,Python解釋器自動創建這個對象。但就和Python中的大多數事情一樣,Python仍然提供給你手動處理的方法。還記得內建函數type嗎?這個古老但強大的函數能夠讓你知道一個對象的類型是什麽,就像這樣:

print type(1)
print type("1")
print type(ObjectCreator)
print type(ObjectCreator())
這裏,type有一種完全不同的能力,它也能動態的創建類。type可以接受一個類的描述作為參數,然後返回一個類。(我知道,根據傳入參數的不同,同一個函數擁有兩種完全不同的用法是一件很傻的事情,但這在Python中是為了保持向後兼容性)
type可以像這樣工作:
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
比如下面的代碼:
class MyShinyClass(object):
pass

MyShinyClass = type(‘MyShinyClass‘, (), {})

返回一個類對象

print MyShinyClass
print MyShinyClass()

創建一個該類的實例

<main.myshinyclass object="" at="" 0x8997cec="">

你會發現我們使用“MyShinyClass”作為類名,並且也可以把它當做一個變量來作為類的引用。類和變量是不同的,這裏沒有任何理由把事情弄的復雜。
type 接受一個字典來為類定義屬性,因此
class Foo(object):
bar = True

可以翻譯為:

Foo = type(‘Foo‘, (), {‘bar‘:True})
為類增加方法。只需要定義一個有著恰當簽名的函數並將其作為屬性賦值就可以了。

def echo_bar(self):
print self.bar

FooChild = type(‘FooChild‘, (Foo,), {‘echo_bar‘: echo_bar})
hasattr(Foo, ‘echo_bar‘)
False
hasattr(FooChild, ‘echo_bar‘)
True
my_foo = FooChild()
my_foo.echo_bar()
True
你可以看到,在Python中,類也是對象,你可以動態的創建類。這就是當你使用關鍵字class時Python在幕後做的事情,而這就是通過元類來實現的。

到底什麽是元類

元類就是用來創建類的“東西”。你創建類就是為了創建類的實例對象,不是嗎?但是我們已經學習到了Python中的類也是對象。好吧,元類就是用來創建這些類(對象)的,元類就是類的類,你可以這樣理解為:

MyClass = MetaClass()
MyObject = MyClass()
你已經看到了type可以讓你像這樣做:

MyClass = type(‘MyClass‘, (), {})

這是因為函數type實際上是一個元類。type就是Python在背後用來創建所有類的元類。現在你想知道那為什麽type會全部采用小寫形式而不是Type呢?好吧,我猜這是為了和str保持一致性,str是用來創建字符串對象的類,而int是用來創建整數對象的類。type就是創建類對象的類。你可以通過檢查class屬性來看到這一點。Python中所有的東西,註意,我是指所有的東西——都是對象。這包括整數、字符串、函數以及類。它們全部都是對象,而且它們都是從一個類創建而來。

>> age = 35
>> age.class
>> name = ‘bob‘
>> name.class
>> def foo(): pass
>>foo.class
>> class Bar(object): pass
>> b = Bar()
>> b.class
現在,對於任何一個classclass屬性又是什麽呢?

>> a.class.class
>> age.class.class
>> foo.class.class
>> b.class.class
因此,元類就是創建類這種對象的東西。如果你喜歡的話,可以把元類稱為“類工廠”(不要和工廠類搞混了:D) type就是Python的內建元類,當然了,你也可以創建自己的元類。

metaclass__屬性

你可以在寫一個類的時候為其添加metaclass屬性。

class Foo(object):
metaclass = something…
[…]
如果你這麽做了,Python就會用元類來創建類Foo。小心點,這裏面有些技巧。你首先寫下class Foo(object),但是類對象Foo還沒有在內存中創建。Python會在類的定義中尋找metaclass屬性,如果找到了,Python就會用它來創建類Foo,如果沒有找到,就會用內建的type來創建這個類。把下面這段話反復讀幾次。當你寫如下代碼時 :
class Foo(Bar):
pass
Python做了如下的操作:
Foo中有metaclass這個屬性嗎?如果是,Python會在內存中通過metaclass創建一個名字為Foo的類對象(我說的是類對象,請緊跟我的思路)。如果Python沒有找到metaclass,它會繼續在Bar(父類)中尋找metaclass屬性,並嘗試做和前面同樣的操作。如果Python在任何父類中都找不到metaclass,它就會在模塊層次中去尋找metaclass,並嘗試做同樣的操作。如果還是找不到metaclass,Python就會用內置的type來創建這個類對象。
現在的問題就是,你可以在metaclass中放置些什麽代碼呢?答案就是:可以創建一個類的東西。那麽什麽可以用來創建一個類呢?type,或者任何使用到type或者子類化type的東東都可以。
自定義元類

元類的主要目的就是為了當創建類時能夠自動地改變類。通常,你會為API做這樣的事情,你希望可以創建符合當前上下文的類。假想一個很傻的例子,你決定在你的模塊裏所有的類的屬性都應該是大寫形式。有好幾種方法可以辦到,但其中一種就是通過在模塊級別設定metaclass。采用這種方法,這個模塊中的所有類都會通過這個元類來創建,我們只需要告訴元類把所有的屬性都改成大寫形式就萬事大吉了。
幸運的是,metaclass實際上可以被任意調用,它並不需要是一個正式的類(我知道,某些名字裏帶有‘class’的東西並不需要是一個class,畫畫圖理解下,這很有幫助)。所以,我們這裏就先以一個簡單的函數作為例子開始。

元類會自動將你通常傳給‘type’的參數作為自己的參數傳入

def upper_attr(future_class_name, future_class_parents, future_class_attr):

‘‘‘返回一個類對象,將屬性都轉為大寫形式‘‘‘

選擇所有不以‘__‘開頭的屬性

attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(‘__‘))

將它們轉為大寫形式

uppercase_attr = dict((name.upper(), value) for name, value in attrs)

通過‘type‘來做類對象的創建

return type(future_class_name, future_class_parents, uppercase_attr)

metaclass = upper_attr

這會作用到這個模塊中的所有類

class Foo(object):

我們也可以只在這裏定義metaclass,這樣就只會作用於這個類中

bar = ‘bip‘

print hasattr(Foo, ‘bar‘)

輸出: False

print hasattr(Foo, ‘BAR‘)

輸出:True

f = Foo()
print f.BAR

輸出:‘bip‘

現在讓我們再做一次,這一次用一個真正的class來當做元類。

請記住,‘type‘實際上是一個類,就像‘str‘和‘int‘一樣

所以,你可以從type繼承

class UpperAttrMetaClass(type):

new 是在init之前被調用的特殊方法

new是用來創建對象並返回之的方法

init只是用來將傳入的參數初始化給對象

你很少用到new,除非你希望能夠控制對象的創建

這裏,創建的對象是類,我們希望能夠自定義它,所以我們這裏改寫new

如果你希望的話,你也可以在init中做些事情

還有一些高級的用法會涉及到改寫call特殊方法,但是我們這裏不用

def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(‘__‘))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    return type(future_class_name, future_class_parents, uppercase_attr)

但是,這種方式其實不是OOP。我們直接調用了type,而且我們沒有改寫父類的new方法。現在讓我們這樣去處理:
class UpperAttrMetaclass(type):
def new(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(‘__‘))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)

復用type.new方法

這就是基本的OOP編程,沒什麽魔法

    return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)

你可能已經註意到了有個額外的參數upperattr_metaclass,這並沒有什麽特別的。類方法的第一個參數總是表示當前的實例,就像在普通的類方法中的self參數一樣。當然了,為了清晰起見,這裏的名字我起的比較長。但是就像self一樣,所有的參數都有它們的傳統名稱。因此,在真實的產品代碼中一個元類應該是像這樣的:
class UpperAttrMetaclass(type):
def new(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith(‘‘)
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return type.new(cls, name, bases, uppercase_attr)
如果使用super方法的話,我們還可以使它變得更清晰一些,這會緩解繼承(是的,你可以擁有元類,從元類繼承,從type繼承)
class UpperAttrMetaclass(type):
def
new(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith(‘
‘))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return super(UpperAttrMetaclass, cls).new(cls, name, bases, uppercase_attr)
用元類實現單例模式

class Singleone(type):

def __init__(cls, name,bases,dct):
    super(Singleone, cls).__init__(name,bases,dct)

    cls._instance = None

def __call__(cls, *args, **kwargs):
    if cls._instance is None:
        cls._instance = super(Singleone,cls).__call__(*args,**kwargs)
    return cls._instance

class One(object):
metaclass = Singleone
def init(self,*args, **kwargs):
self.name = args[0]

o = One("a")
print o.name
b = One("b")
print b.name

執行步驟從上到下當執行用Singleone創建One類,One = Singleone(...),當實例化Singleone的時候執行其內部的init()方法,代碼
繼續解析到o = One("a")的時候示例化了One類,相當於Singleone()(),此時執行Singleone裏面的call方法,在call方法裏需要有
返回值。也可以在call裏主動實例化(new)One並調用裏面的init方法。

就是這樣,除此之外,關於元類真的沒有別的可說的了。使用到元類的代碼比較復雜,這背後的原因倒並不是因為元類本身,而是因為你通常會使用元類去做一些晦澀的事情,依賴於自省,控制繼承等等。確實,用元類來搞些“黑暗魔法”是特別有用的,因而會搞出些復雜的東西來。
為什麽要用metaclass類而不是函數?
由於metaclass可以接受任何可調用的對象,那為何還要使用類呢,因為很顯然使用類會更加復雜啊?這裏有好幾個原因:
1) 意圖會更加清晰。當你讀到UpperAttrMetaclass(type)時,你知道接下來要發生什麽。
2) 你可以使用OOP編程。元類可以從元類中繼承而來,改寫父類的方法。元類甚至還可以使用元類。
3) 你可以把代碼組織的更好。當你使用元類的時候肯定不會是像我上面舉的這種簡單場景,通常都是針對比較復雜的問題。將多個方法歸總到一個類中會很有幫助,也會使得代碼更容易閱讀。
4) 你可以使用new, init以及call這樣的特殊方法。它們能幫你處理不同的任務。就算通常你可以把所有的東西都在new裏處理掉,有些人還是覺得用init更舒服些。
5) 哇哦,這東西的名字是metaclass,肯定非善類,我要小心!

“元類就是深度的魔法,99%的用戶應該根本不必為此操心。如果你想搞清楚究竟是否需要用到元類,那麽你就不需要它。那些實際用到元類的人都非常清楚地知道他們需要做什麽,而且根本不需要解釋為什麽要用元類。” —— Python界的領袖 Tim Peters
元類的主要用途是創建API。一個典型的例子是Django ORM。它允許你像這樣定義:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
這並不會返回一個IntegerField對象,而是會返回一個int,甚至可以直接從數據庫中取出數據。這是有可能的,因為models.Model定義了metaclass, 並且使用了一些魔法能夠將你剛剛定義的簡單的Person類轉變成對數據庫的一個復雜hook。Django框架將這些看起來很復雜的東西通過暴露出一個簡單的使用元類的API將其化簡,通過這個API重新創建代碼,在背後完成真正的工作。

結語
首先,你知道了類其實是能夠創建出類實例的對象。好吧,事實上,類本身也是實例,當然,它們是元類的實例。
Python中的一切都是對象,它們要麽是類的實例,要麽是元類的實例,除了type。type實際上是它自己的元類,在純Python環境中這可不是你能夠做到的,這是通過在實現層面耍一些小手段做到的。其次,元類是很復雜的。對於非常簡單的類,你可能不希望通過使用元類來對類做修改。你可以通過其他兩種技術來修改類:
1) Monkey patching
2) class decorators
當你需要動態修改類時,99%的時間裏你最好使用上面這兩種技術。當然了,其實在99%的時間裏你根本就不需要動態修改類

2 私有變量(__xx)

    python類裏的私有變量就是前面加兩個下劃線這樣用,但是這只是在使用上的私有變量,不像Java那種只能通過內部函數修改,python的私有變量可以通過 對象._類名__參數來從外部引用。

3 type

    請查看 1.20

4 推導式

    推導式又稱解析式,有三種

    1,列表推導式

            multiples = [ i for i in range(30) if i % 3 is 0 ]

    2,字典推導式

            mcase = {"a":10,"b":2,"c":3}

            {k:v for k,v in mcase.items()}

    3,集合推導式

            其實大括號裏擴著的就是集合(set),例:

                {"a","b",1}

            squared = {x*2 for x in [1,2,3]}

5 裝飾器(@decorate)

      裝飾器是python特色代表之一,非常好用,先介紹一下如何用裝飾器。

       函數是可以返回函數的

def hi(name="yasoob"):
def greet():
return "in greet() function"
def welcome():
return "in welcome() function"
if name == "yasoob":
return greet
else:
return welcome

a = hi()
print a

<function greet at 0x1e08410>

    在if/else裏面我們返回greet和welcome,而不是greet()和welcome(),為什麽? 是因為當把小括號放到後面的時候這個函數就會執行,如果不放小括號這個函數就可以到處傳遞,並且可以賦給變量而不去執行。

    將函數作為參數傳遞給另一個函數

def hi():
return "hi yasoob"

def doSomethingBefore(func):
print "I am doing something before"
print (func())

doSomethingBefore(hi)
輸出:

    I am doing something before

    hi yasoob

裝飾器就是在一個函數前後執行代碼

上個例子裏我們相當於創建了裝飾器,現在我們稍加修改並編寫一個更有用的程序。

def a_new_decorator(a_func):
def wrapTheFunction():
print "I am doing some before"
a_func()
print "I am doing some after"
return wrapTheFunction

def a_function_requiring_decoration():
print "I am in the function"

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
明白了嗎? 這正是python裝飾器做的事情,它們封裝一個函數,並且用這樣或者那樣的方式修改它的行為,現在你可能疑惑,我們的代碼裏並沒有使用@符號?那只是一個簡短的方式來生成一個被裝飾的函數。請見如下例子

@a_new_decorator
def a_function_requiring_decoration():
print "I am in the function"

a_function_requiring_decoration()
現在對裝飾器的理解差不多了吧!但如果我們運行如下代碼會存在一個問題:

print(a_function_requiring_decoration.name)

輸出:wrapTheFunction

這並不是我們想要看到的,我們想看到的是a_function_requiring_decoration,這裏的函數被wrapTheFunction替代了,它重寫了我們函數的名字和註釋文檔(docstring)。幸運的是python提供給我們一個簡單的函數來解決這個問題

from functools import wraps
def a_new_decorator(a_func):@wraps(a_func)
br/>@wraps(a_func)
print "I am doing some before"
a_func()
print "I am doing some after"
return wrapTheFunction
下面我們看一下藍本規範:

from functools import wraps
def decorator_name(f):@wraps(f)
br/>@wraps(f)
kwargs):
if not can_run:
return "Function will not run"
return f(args, **kwargs)
return decorated

@decorator_name
def func():
return "Function is running"
can_run = True
print(func())
can_run = False
print(func())
註意:@wraps接受一個函數來進行裝飾,並加入了復制函數名稱,註釋文檔,參數列表等等的功能。這可以讓我們在裝飾器裏面訪問在裝飾器之前的函數的屬性。

裝飾器的使用場景:

    授權(Authorization)

        裝飾器能有助於檢查某個人是否被授權去使用一個web應用的端點(endpoint)。它們被大量使用於Flask和Django框架中。這裏是一個例子來使用基於裝飾器的授權:

from functools import wraps
def requires_auth(f):@wraps(f)
br/>@wraps(f)
kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username,auth.password):
authenticate()
return f(args,**kwargs)
return decorated
日誌(Logging)

from functools import wraps
def logit(func):@wraps(func)
br/>@wraps(func)
kwargs):
print(func.name + " was called")
return func(args, **kwargs)
return with_logging@logit
br/>@logit
return x+x
addition_func(2)
在函數中嵌入裝飾器

        我們回到日誌的例子,並創建一個包裹函數,能讓我們指定一個用於輸出的日誌文件。

from functools import wraps
def logit(logfile=‘out.log‘):
def logging_decorator(func):@wraps(func)
br/>@wraps(func)
kwargs):
log_string = func.name + " was called"
print(log_string)
with open(logfile, ‘wb‘) as f:
f.write(log_string + ‘\n‘)
return func(args, **kwargs)
return wrapped_function
return logging_decorator

@logit
def myfunc():
pass
裝飾器類

        現在我們有了能用於正式環境的logit裝飾器,但當我們的應用的某些部分還比較脆弱時,異常也許是需要更緊急關註的事情。比方說有時候你只想打日誌到一個文件,而有時你想把引起你註意的問題發送到一個email,同事也保留日誌,留個記錄。這是一個使用繼承的場景,但目前為止我們只看到過用來構建裝飾器的函數。

        The lucky is! 類也可以構建裝飾器,現在我們用類重新構建logit

class logit(object):
def init(self, logfile=‘out.log‘):
self.logfile = logfile

def __call__(self, func):
    @wraps(func)
    def wrapped_function(*args, **kwargs):
        log_string = func.__name__ + " was called"
        print(log_string)
        with open(self.logfile, ‘wb‘) as f:
            f.write(log_string + ‘\n‘)
        self.notify()
        return func(*args,**kwargs)
    return wrapped_function

def notify(self):
    # 可以做一些其它行為
    pass

@logit()
def my_func():
pass
現在我們給logit創建子類,來添加email等功能

class email_logit(logit):

def __init__(self, email=‘[email protected]‘, *args, **kwargs):
    self.email = email
    super(email_logit, self).__init__(*args, **kwargs)

def notify(self):
    # 發送一封email
    pass

從現在起,@email_logit會在logit基礎上多發送一封郵件。

註意:從以上方法中我們就可以發現call這種用法的好處,它在裝飾器類和新寫元類的時候起到了很大作用。

6 容器

    python附帶一個模塊,它包含許多容器數據類型,名字叫做collections。我們將討論它的作用和用法。

    defaultdict:

        defaultdict不需要檢查key是否存在,我們一般這樣用

from collections import defaultdict
ddl = defaultdict(list)
ddl["x"].append(1)
print ddl
ddd = defaultdict(dict)
ddd["x"]["a"] = 1
print ddd

defaultdict(<type ‘list‘>, {‘x‘: [1]})
defaultdict(<type ‘dict‘>, {‘x‘: {‘a‘: 1}})
Counter

        counter是一個計數器,幫助我們對某項數據做統計。

from collections import Counter
c = Counter("aaaabbbc")
print c

d = {"a":1,"b":2,"c":3}
c = Counter( k for k,v in d.items())
print c
還可以用counter來統計一個文件

                此處沒有弄明白,需要後期補上

    deque

         deque提供了一個雙向隊列,可以從頭尾兩端添加或刪除元素,類似於list

from collections import deque
dl = deque(range(5))
print dl
dl.popleft()
print dl
dl.pop()
print dl
dl.extendleft([-10])
print dl
dl.extend([10])
print dl
輸出:

deque([0, 1, 2, 3, 4])
deque([1, 2, 3, 4])
deque([1, 2, 3])
deque([-10, 1, 2, 3])
deque([-10, 1, 2, 3, 10])
deque也可以限制列表的大小,先進先出

dl = deque(maxlen=2)
dl.append(1)
dl.append(2)
print dl
dl.append(3)
print dl
輸出

deque([1, 2], maxlen=2)
deque([2, 3], maxlen=2)
namedtuple(命名元組)

正常訪問一個元組和訪問list一樣,都是通過下標來訪問,命名元組可以提供類似於字典的訪問方式,和tuple一樣不可變。

from collections import namedtuple
Animal = namedtuple(‘Animal‘,‘name age type‘)
perry = Animal(name=‘perry‘,age=10,type=‘cat‘)
print perry
print perry.name
一個命名元組需要兩個參數,他們是元組名稱和字段名稱。在上面的例子中,我們的元組名稱是Animal,字段名稱是‘name,age,type‘。

namedtuple讓你的元組變得自文檔了。不必使用證書索引來訪問一個命名元組,這讓代碼更易於維護。

而且,namedtuple的每個實例沒有對象字典(dict),所以它們更輕量,與普通的元組相比,並不需要更多的內存,這使他們比字典更快。

然而,要記住它仍然是一個元組,屬性在namedtuple中是不可變的,所以下面的代碼不行:

perry.age = 10
命名元組(namedtuple)向後兼容元組,所以用下標訪問也是可以的

print perry[0]
命名元組支持多態,可以轉換為字典

print (perry._asdict())
7 上下文

    上下文管理器允許你在需要的時候,精確的分配和釋放資源。

    使用上下文管理器最廣泛的案例就是with語句。想象一下你有個需要結對執行的操作,然後還要在中間放置一段代碼。

    上下文管理器就是專門讓你做這種事情的,舉個例子:

with open(‘some_file‘, ‘wb‘) as f:
f.write("fuck u!")
上面這段代碼打開了一個文件,往裏面寫入了一些數據,然後關閉該文件。如果在往文件裏寫數據的時候發生異常,它也會嘗試去關閉文件。上面的代碼與下面的是等價的。

file = open(‘some_file‘, ‘wb‘)
try:
file.write("funck u !")
finally:
file.close()
當與第一個例子比較的時候,有很多樣板代碼(boilerplate code)被消掉了。這就是with語句的主要優勢,它確保我們的文件會被關閉,而不用關註嵌套代碼如何退出。

上下文的又一用例就是資源的加鎖與解鎖,以及關閉已經打開的文件(就像上面的例子)

下面讓我們自己實現一下上下文管理器

 一個上下文管理器的類,最起碼要定義__enter__,__exit__方法。

class File(object):
def init(self,file_name, method):
self.file_obj = open(file_name, method)
def enter(self):
return self.file_obj
def exit(self, exc_type, exc_val, exc_tb):
self.file_obj.close()

with File(‘demo.txt‘, ‘wb‘) as f:
f.write(‘Hello‘)
我們的exit函數接受三個參數。這些參數對於每個上下文管理器類中的exit方法都是必須得,我們來談談在底層都發生了什麽。

1,with語句先暫存了File類的exit方法

2,然後它調用File類的enter方法

3,enter方法返回打開文件對象

4,打開的文件對象被傳遞給 f

5,使用write來寫文件

6,調用之前暫存的exit

7,exit關閉了文件

    處理異常

    我們目前還沒有談到__exit__方法的這三個參數,exc_type,exc_val,exc_tb,在with以下部分如果發生異常,python會將異常的type,value和traceback傳遞給__exit__方法。

    它讓__exit__方法來決定如何關閉文件以及是否需要其他步驟,如果沒有異常這三個參數的值為None

class File(object):
def init(self,file_name, method):
self.file_obj = open(file_name, method)
def enter(self):
return self.file_obj
def exit(self, exc_type, exc_val, exc_tb):
print exc_val
self.file_obj.close()
return True

with File(‘demo.txt‘, ‘wb‘) as f:
f.write_function(‘Hello‘)
當發生異常的時候with語句會采取如下步驟:

1,它把異常的type,value,traceback傳遞給exit方法

2,它讓exit方法來處理異常

3,如果exit返回的是True,那麽這個異常就優雅的處理了

4,如果exit返回的是除了True以外的其它值,那麽這個異常會被拋出

基於裝飾器和生成器來實現上下文管理

    python有個contextlib專門用於這個,我們可以使用一個生成器函數來實現一個上下文管理器,而不是使用一個類。

from contextlib import contextmanager@contextmanager
br/>@contextmanager
f = open(name,‘w‘)
yield f
f.close()

with open_file(‘aaa.log‘) as of:
of.write("fuck u!")
這塊我個人用得比較少,因為內部也是通過enterexit來實現的。

8 繼承

9 生成器,叠代器,可叠代對象
10 自省
11 閉包
12 反射
13 推導式
14 C擴展
15 函數緩存
16 Flask上下文
18 多線程,多進程,協程
20 socket和socketserver
21 yield
22 進程間通訊
23 進程內存共享
24 Python實現冒泡法排序
25 對象

26 繼承

io多路復用

27 動態加載

Python 模塊動態加載技術

C 語言中可以使用 dlopen,dlsym 和 dlclose 讓程序在運行過程中按需加載和卸載動態庫。Python 也支持這種方式,使用模塊動態加載技術,我們可以把程序的配置文件寫成可運行的 python 程序,在程序運行過程中可以動態去更新配置。當然也可以將 python 腳本作為業務邏輯加載到正在運行的主程序中,而不用重啟服務。

作者在個人項目 pyed 中使用了這種技術,本文對個人研究和使用這種技術的一個總結。如有問題,歡迎大家討論。

在 Python 中執行代碼

python 提供了 exec 用於在程序中執行一段 python 代碼,官方說明:

exec_stmt ::= "exec" or_expr ["in" expression ["," expression]]
該語句可以使用 exec() 函數進行替代。來看一個簡單的例子:

>> exec "print(‘Hello World‘)"
Hello World
>>
這種使用方式,在程序中其實作用不大,我們使用動態加載,一般是希望將一個模塊中的某個變量或函數按需引入到正在執行的程序中,而不僅僅是去執行一下,打印一句 “Hello World”,exec 中的 in 解決了這個問題。

in 的作用是將執行代碼中的變量,函數或者類放入到一個字典中,這裏再來看一個例子:

>> exec "a=100" in tmp
>> print tmp
{‘builtins‘: ..., ‘a‘: 100}
>>
上面的語句等效於:

exec("a=100", tmp)
執行結果中,tmp 除了我們給定的一個 a 變量,賦值為 100 外,還有一個 builtins 成員,內容很多,這裏使用 … 替代了實際的內容。如果要訪問 a 的值,只需要像操作字典一樣就行了:

>> print tmp["a"]
100
>>
簡單的模塊加載

簡單模塊加載庫

按照上面的思路,我們構造了一個模塊

import traceback

class loader(object):
def init(self):
pass

def load(self, path):
    try:
        tmp = {}
        exec open(path).read() in tmp
        return tmp
    except:
        print("Load module [path %s] error: %s"
              % (path, traceback.format_exc()))
        return None

加載配置文件

有一個配置文件 test.conf:

$ cat test.conf
addr="127.0.0.1"
port=2539

$
使用以下代碼加載它:

load = loader()
m = load.load("test.conf")
addr = m["addr"]
port = m["port"]
print addr + ":" + str(port)
執行結果:

$ python loader.py
127.0.0.1:2539
$
加載和執行函數

如果要執行加載模塊(test.py)中的函數:

def greeting(name):
print "Hello", name
使用以下代碼加載它:

load = loader()
m = load.load("test.py")
func = m["greeting"]
func("World")
執行結果:

$ python loader.py
Hello World
$
加載和使用模塊中的類

按照上面的思路,如果加載的模塊是一個類,其實調用方式也是大同小異的。

修改 test.py

class test(object):
def init(self):
pass

def greeting(self, name):
    print "Hello", name

使用以下代碼加載它:

load = loader()
m = load.load("test.py")
c = m["test"]
print c
print dir(c)
t = c()
t.greeting("World")
執行結果:

$ python loader.py
<class ‘test‘>
[‘class‘, ‘delattr‘, ‘dict‘, ‘doc‘, ‘format‘, ‘getattribute‘, ‘hash‘, ‘init‘, ‘module‘, ‘new‘, ‘reduce‘, ‘__reduce_ex‘, ‘repr‘, ‘setattr‘, ‘sizeof‘, ‘str‘, ‘subclasshook‘, ‘weakref__‘, ‘greeting‘]
Hello World
$
從上面可以看到 m[“test”] 是一個 class 類型,我們可以使用它創建類的實例,並調用實例方法

加載的模塊引入了其它模塊

如果在加載的模塊中導入了其它模塊,調用方法也是不變的。我們引入一個 test1,繼承上例中的 test:

from test import test

class test1(test):
def init(self):
test.init(self)
使用以下代碼加載它:

load = loader()
m = load.load("subtest.py")
c = m["test1"]
print c
print dir(c)
t = c()
t.greeting("World")
執行結果:

$ python loader.py
<class ‘test1‘>
[‘class‘, ‘delattr‘, ‘dict‘, ‘doc‘, ‘format‘, ‘getattribute‘, ‘hash‘, ‘init‘, ‘module‘, ‘new‘, ‘reduce‘, ‘__reduce_ex‘, ‘repr‘, ‘setattr‘, ‘sizeof‘, ‘str‘, ‘subclasshook‘, ‘weakref__‘, ‘greeting‘]
Hello World
$
改進模塊加載

上一節介紹了使用 exec … in … 的方式動態去加載模塊。完成後可以直接使用返回的字典,訪問模塊中的變量,函數和類。但是從習慣上,我們更習慣使用模塊去調用模塊中的變量,函數和類,按此思路,我們對前面的模塊加載器進行修改。

新的模塊加載器

import traceback, types

class loader(object):
def init(self):
pass

def load(self, name, path):
    try:
        m = types.ModuleType(name)
        exec open(path).read() in m.__dict__
        return m
    except:
        print("Load module [path %s] error: %s"
              % (path, traceback.format_exc()))
        return None

這裏使用 types.ModuleType 來構造一個模塊 m,將 exec 生成的字典放入到 m.dict。這樣就生成了一個簡單的模塊

使用新的模塊加載器

待加載的模塊:

def test():
s = 0
for i in range(1000000):
s += i
print s
執行邏輯:

load = loader()
m = load.load("test", "test.py")
print m
print m.dict
m.test()
執行結果:

$ python loader.py
<module ‘test‘ (built-in)>
{‘builtins‘: ..., ‘name‘: ‘test‘, ‘test‘: <function test at 0x1007f7398>, ‘doc‘: None}
499999500000
$
從執行結果,我們可以看到使用新的模塊加載器,我們得到的是一個 module 類型的實例,其 dict 中包含了 test 函數,我們可以直接使用 m.test() 調用該函數

大發彩_票平臺搭建