Python新式類 單例模式與範圍(四)

來源:互聯網
上載者:User

標籤:property   建立   elf   UNC   模式設計   range   通用   元組   mys   

1 新式類與舊式類

新式類擁有經典類的全部特性之外,還有一些新的特性,比如 __init__發生變化,新增了靜態方法__new__,python3目前都採用新式類,新式類是廣度優先,舊式類是深度優先

#新式類class C(object):    pass#經典類class B:    pass
(1)內建的object對象
1. __new__,__init__方法這兩個方法是用來建立object的子類對象,靜態方法__new__()用來建立類的執行個體,然後再調用__init__()來初始化執行個體。 2. __delattr__, __getattribute__, __setattr__方法對象使用這些方法來處理屬性的訪問 3. __hash__, __repr__, __str__方法print(someobj)會調用someobj.__str__(), 如果__str__沒有定義,則會調用someobj.__repr__(), __str__()和__repr__()的區別:預設的實現是沒有任何作用的__repr__的目標是對象資訊唯一性__str__的目標是對象資訊的可讀性容器物件的__str__一般使用的是對象元素的__repr__如果重新定義了__repr__,而沒有定義__str__,則預設調用__str__時,調用的是__repr__也就是說好的編程習慣是每一個類都需要重寫一個__repr__方法,用於提供對象的可讀資訊,而重寫__str__方法是可選的。實現__str__方法,一般是需要更加好看的列印效果,比如你要製作一個報表的時候等。
(2)類的方法

靜態方法

靜態方法可以被類或者執行個體調用,它沒有常規方法的行為(比如綁定,非綁定,預設的第一個self參數),當有一堆函數僅僅是為了一個類寫的時候,採用靜態方法聲明在類的內部,可以提供行為上的一致性。使用裝飾符@staticmethod進行建立 

類方法

也是可以通過類和它的執行個體進行調用,不過它是有預設第一個參數,叫做是類對象,一般被命名為cls,當然你也可以命名為其它名字,這樣就你可以調用類對象的一些操作,代碼如下,使用裝飾符@classmethod建立:

新式類(new-style-class)

__init__方法: 類的初始化方法

__new__靜態方法

新式類都有一個__new__的靜態方法,它的原型是object.__new__(cls[, ...])

cls是一個類對象,當你調用C(*args, **kargs)來建立一個類C的執行個體時,python的內部調用是

C.__new__(C, *args, **kargs),然後傳回值是類C的執行個體c,在確認

c是C的執行個體後,python再調用C.__init__(c, *args, **kargs)來初始化執行個體c。

所以調用一個執行個體c = C(2),實際執行的代碼為:

c = C.__new__(C, 2)if isinstance(c, C):    C.__init__(c, 23)#__init__第一個參數要為執行個體對象    class Singleton(object):    _singletons = {}    def __new__(cls):        if not cls._singletons.has_key(cls):            #若還沒有任何執行個體            cls._singletons[cls] = object.__new__(cls)  #產生一個執行個體        return cls._singletons[cls]                     #返回這個執行個體    a = Singleton()    b = Singleton()    id(a)  #35966666    id(b)  #35966666        #注:單例模式 ,兩個執行個體指向同一個記憶體位址 
(3)新式類執行個體

新式類的執行個體也具有新的特性。比如它擁有Property功能,該功能會對屬性的訪問方式產生影響;還有__slots__新屬性,該屬性會對產生子類執行個體產生影響;還添加了一個新的方法__getattribute__,比原有的__getattr__更加通用。

__slots__屬性

通常每一個執行個體x都會有一個__dict__屬性,用來記錄執行個體中所有的屬性和方法,也是通過這個字典,

可以讓執行個體綁定任意的屬性。而__slots__屬性作用就是,當類C有比較少的變數,而且擁有__slots__屬性時,

類C的執行個體 就沒有__dict__屬性,而是把變數的值存在一個固定的地方。如果試圖訪問一個__slots__中沒有

的屬性,執行個體就會報錯。這樣操作有什麼好處呢?__slots__屬性雖然令執行個體失去了綁定任意屬性的便利,

但是因為每一個執行個體沒有__dict__屬性,卻能有效節省每一個執行個體的記憶體消耗,有利於產生小而精

乾的執行個體。

定義__slots__屬性

class A(object):    def __init__(self):        self.x = 1        self.y = 2        __slots__ = (‘x‘,‘y‘)a = A()a.z = 3a.u = 4  #都會報錯,不能對執行個體新增屬性,__dict__字典集沒有任何改變class A(object):    def __init__(self):        self.x = 1        self.y = 2a = A()a.x = 3  #不會報錯,在__dict__字典字典集會新增‘x‘屬性

使用時__slots__時需要注意的幾點:

1.  當一個類的父類沒有定義__slots__屬性,父類中的__dict__屬性總是可以訪問到的,所以只在子類中定義__slots__屬性,而不在父類中定義是沒有意義的。 2. 如果定義了__slots__屬性,還是想在之後添加新的變數,就需要把‘__dict__‘字串添加到__slots__的元組裡。3. 定義了__slots__屬性,還會消失的一個屬性是__weakref__,這樣就不支援執行個體的weak reference,如果還是想用這個功能,同樣,可以把‘__weakref__‘字串添加到元組裡。4. __slots__功能是通過descriptor實現的,會為每一個變數建立一個descriptor。5. __slots__的功能隻影響定義它的類,因此,子類需要重新定義__slots__才能有它的功能。
__getattribute__方法

對新式類的執行個體來說,所有屬性和方法的訪問操作都是通過__getattribute__完成,這是由object基類實現的。如果有特殊的要求,可以重載__getattribute__方法.

2 __init____new__區別
  1. __new__是一個靜態方法,而__init__是一個執行個體方法.
  2. __new__方法會返回一個建立的執行個體,而__init__什麼都不返回.
  3. 只有在__new__返回一個cls的執行個體時後面的__init__才能被調用.
  4. 當建立一個新執行個體時調用__new__,初始化一個執行個體時用__init__.

__metaclass__是建立類時起作用.所以我們可以分別使用__metaclass__,__new____init__來分別在類建立,執行個體建立和執行個體初始化的時候做一些操作

3 單例模式

? 單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個執行個體而且該執行個體易於外界訪問,從而方便對執行個體個數的控制並節約系統資源,系統中存在多個該類的執行個體對象,而這樣會嚴重浪費記憶體資源 如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。

__new__()__init__()之前被調用,用於產生執行個體對象。利用這個方法和類的屬性的特點可以實現設計模式的單例模式。單例模式是指建立唯一對象,單例模式設計的類只能執行個體一次

一般python中可以採用以下方法實現單例模式

  • 使用模組(import匯入)
  • 使用 __new__
  • 使用裝飾器(decorator)
  • 使用元類(metaclass)

(1)使用__new__方法

class Singleton(object):    def __new__(cls, *args, **kw):        if not hasattr(cls, ‘_instance‘): #如果該類是否含有執行個體            orig = super(Singleton, cls)            cls._instance = orig.__new__(cls, *args, **kw)        return cls._instanceclass MyClass(Singleton):    a = 1#如果 cls._instance 為 None 則建立執行個體,否則直接返回 cls._instance。

(2)共用屬性(使用metaclass)

元類(metaclass)可以控制類的建立過程,它主要做三件事:

  • 攔截類的建立
  • 修改類的定義
  • 返回修改後的類

建立執行個體時把所有執行個體的__dict__指向同一個字典,這樣它們具有相同的屬性和方法.

class Borg(object):    _state = {}    def __new__(cls, *args, **kw):        ob = super(Borg, cls).__new__(cls, *args, **kw)        ob.__dict__ = cls._state        return obclass MyClass2(Borg):    a = 1

(3) 裝飾器版本

def singleton(cls):    instances = {}    def getinstance(*args, **kw):        if cls not in instances:            instances[cls] = cls(*args, **kw)        return instances[cls]    return getinstance@singletonclass MyClass:  ...

(4) import 方法(使用模組)

作為python的模組是天然的單例模式

# mysingleton.pyclass My_Singleton(object):    def foo(self):        passmy_singleton = My_Singleton()# to usefrom mysingleton import my_singleton #將類進行匯入使用my_singleton.foo()
4 Python範圍

python的範圍分全域和局部,python變數的範圍分為以下四類:

L(local) 局部範圍
E(Enclosing) 閉包函數外的函數中
G(Global) 全域範圍
B(Built-in) 內建範圍

(1) 局部範圍

在瞭解局部範圍之前,先瞭解下塊級範圍的概念

#塊級範圍if 1 == 1:    name = "lol"print(name) for i in range(10):    age = iprint(age)#輸出:C:/Users/L/PycharmProjects/s14/preview/Day8/範圍/test.pylol9

可以發現python代碼運行ok,為什麼外部可以調用內部的變數呢? 因為在python中沒有塊級範圍的概念,代碼塊裡的變數,外部可以調用,所以可運行成功, 也就是說,類似條件判斷(if…..else)、迴圈語句(for x in data)、異常捕捉(try…catch)等的變數是可以全域使用的 .但是不區分範圍明顯是不行的,因此python引入局部範圍

#局部範圍def  func():    name = "lol"func()  #調用函數print(name)#輸出Traceback (most recent call last):  File "C:/Users/L/PycharmProjects/s14/preview/Day8/範圍/test.py", line 23, in <module>    print(name)NameError: name ‘name‘ is not defined

即使執行了一下函數,name的範圍也只是在函數內部,外部依然無法進行調用 ,因此函數可以產生局部範圍,在python中模組(module),類(class)、函數(def、lambda)會產生新的範圍 .

(2) 範圍鏈

#範圍鏈name = "張三"def func1():    name = "李四"    def func2():        name = "王五"        print(name)    func2()func1()#輸出"王五"

func1()調用函數執行變數賦值操作,當調用func2()時 , print(name)中name屬性會先從局部範圍開始尋找,由內至外,最先找到的當然是 ‘王五‘.Python中有範圍鏈,變數會由內到外找,先去自己範圍去找,自己沒有再去上級去找,直到找不到報錯 .

(3) 全域範圍與內建範圍

x = int(2.9)  # 內建範圍num = 0  # 全域範圍def outer():    num2 = 1  # 閉包函數外的函數中    def inner():        num3 = 2  # 局部範圍

(4) global關鍵字

全域變數是指在函數外的變數,可以在程式全域使用,局部變數是指定義在函數內的變數,只能在函數內被聲明使用若內部範圍的想要修改外部範圍的變數,就要使用global關鍵字

a = 100def demo():    global a    a = 123    print(a)demo()print(a)#運行結果是123123

本來運行結果應該是123,100,但是因為global聲明a為全域範圍,因此在執行賦值後的a指向123,因此兩次列印的都是123.

總結下:

? Python 中,一個變數的範圍總是由在代碼中被賦值的地方所決定的。當 Python 遇到一個變數的話他會按照這樣的順序進行搜尋:本地範圍(Local)→當前範圍被嵌入的本地範圍(Enclosing locals)→全域/模組範圍(Global)→內建範圍(Built-in)

Python新式類 單例模式與範圍(四)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.