Python中,定義類創建和使用實例,單下劃線和雙下劃線,__init__構造函數等

定義類創建實例
舉個例子,

class Foo(object):
    def __init__(self, x, y=0):
        self.x = x
        self.y = y
foo = Foo(1,y=2)

對Foo的調用到底調用了什麼函數或方法?
第一反應肯定是__init__方法,但仔細想想並不是正確答案,因爲它沒有返回一個對象,但是調用Foo(1,y=2)確實返回了一個對象,其實它的調用順序是這樣的:

  • 第一步,Foo(1,y=2)等價於Foo.call(1,y=2)
  • 第二步,既然Foo是一個type的實例,Foo.call(1,y=2)實際調用的是type.call(Foo,1,y=2)
  • 第三步,type.call(Foo,1,y=2)調用type.new(Foo,1,y=2),然後返回一個對象。
  • 第四步,obj隨後通過調用obj.init(1,y=2)被初始化。
  • 第五步,obj被返回。

總的來說,__new__方法爲對象分配了內存空間,構建它爲一個“空"對象然後__init__方法被調用來初始化它。
讓我們來看看__new__方法,它負責實際對象的創建方法,分配內存空間並返回該對象。用它來實現單例模式是很方便的。

"""
__new__方法實現單例模式
"""
class A(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance
s1 = A()
s2 = A()
print(s1 is s2)  # True

再來看,如何創建類的屬性

class A(object):  # 使用class定義類,所有的類都是從object類繼承

    version = 1.0  # 類的屬性直接在類的內部定義,當實例屬性和類屬性重名時,實例屬性優先級高、
    
    def __init__(self,a,b):
        self.a = a
        self.b = b
        
if __name__ == '__main__':
    a = A(10,20)
    print(A.version)  # 直接通過類.屬性訪問
    print(a.version)  # 也可以通過實例.屬性訪問
    # 類的屬性可以動態修改
    A.version = '1.3'
    print(A.version)
    # 類的屬性一經修改,所有訪問的屬性值也隨之修改
    print(a.version)
 

實例的創建
創建實例使用類名+(),類似函數調用的形式創建

class A(object):
	pass
a = A()  # 創建實例
a.name = 'frank'  # 創建實例屬性

初始化實例屬性

class A(object):
    version = 2.0  # 定義類屬性
    def __init__(self,name,age):  # self代表實例,通過self訪問實例對象的變量和函數
        self.name = name
        self.__age = age  # 實例的私有屬性無法從外部訪問,從類的內部是可以訪問的
    # 定義實例方法
    def get_age(self):
        return self.__age  # 實例方法,定義在類內部,可以訪問實例的私有屬性__age
    # 定義類方法
    @classmethod
    def how_many(cls):  # 類方法的第一個參數爲cls,cls.version相當於A.version
        return cls.version  # 類方法中無法調用任何實例的變量,只能獲得類引用

p1 = A('frank',23)
print(p1.get_age())  # 實例方法的調用,self不需要顯式傳入

順帶引申一下單下劃線雙下劃線的區別:

以單下劃線開頭(foo)的代表不能直接訪問的類屬性,需通過類提供的接口進行訪問,那麼以“”開頭的名稱都不會被導入,即不能用“from xxx import *”而導入,除非模塊或包中的“all”列表顯式地包含了它們;以雙下劃線開頭的(__foo)代表類的私有成員,只有類本身能訪問,其子類對象也不能訪問到這個數據。

“單下劃線” 開始的成員變量叫做保護變量,意思是隻有類對象和子類對象自己能訪問到這些變量;”雙下劃線” 開始的是私有成員,意思是隻有類對象自己能訪問,連子類對象也不能訪問到這個數據。

舉個例子,

class Foo(object):
    def __init__(self):
        pass
    def public_method(self):
        print('this is public method')
    def __fullprivate_method(self):
        print('this is full private method')
    def _halfprivate_method(self):
        print('this is half private method')

f = Foo()
print(f.public_method())  # OK
print(f._halfprivate_method())  # OK
# print(f.__fullprivate_method())  # AttributeError: 'Foo' object has no attribute '__fullprivate_method'
print(f._Foo__fullprivate_method())  # OK

f._halfprivate_method()可以直接訪問,根據python的約定,應該將其視作private,而不要在外部使用它們。

以單下劃線開頭_foo的代表不能直接訪問的類屬性,需通過類提供的接口進行訪問,不能用“from xxx import *”而導入;以雙下劃線開頭的__foo代表類的私有成員;以雙下劃線開頭和結尾的__foo__代表python裏特殊方法專用的標識,如 init()代表類的構造函數。

__init__構造函數

在定義一個類時,什麼時候用__init__函數,什麼時候不用,用不用有什麼區別?

首先__init__是爲了初始化用的,但是初始化的時候不一定要用這個,直接定義也是可以的,比如

class A(object):
	test_a = '123'

而我們用__init__的好處在於可以接受任何參數並初始化

def __init__(self,a):
	test_a = a

這樣類可以初始化一個動態的變量,更加靈活,直接test(‘123’)就將test_a初始化成123了
再舉一個例子,如下

# 不用init方法定義類
class Rectangle(object):
    def getPeri(self,a,b):
        return (a+b)*2
    def getArea(self,a,b):
        return a*b
rec = Rectangle()
print(rec.getPeri(3,4))  # 14 
print(rec.getArea(3,4))  # 12
print(rec.__dict__)  # {}

從上例可以看到,沒有定義__init__方法,也能得到周長和麪積,但是通過rec.__dict__來看這個實例的屬性,是空的,這就是沒有定義__init__的原因。
並且,在實例化對象時,rec=Rectangle()參數爲空,沒有指定a,b的值,只有在調用函數的時候才指定,且類中定義的每個方法的參數都有a,b,有點多餘了。
因此,需要在類中定義__init__方法,方便創建實例的時候,需要給實例綁定上屬性,也方便類中的方法定義。
上述同樣的例子,用__init__方法定義類,如下

# 使用__init__方法定義類
class Rectangle(object):
    def __init__(self,a,b):
        self.a = a
        self.b = b
    def getPeri(self):
        return (self.a+self.b)*2
    def getArea(self):
        return self.a*self.b

rec = Rectangle(3,4)
print(rec.getPeri())  # 14
print(rec.getArea())  # 12
print(rec.__dict__)  # {'a': 3, 'b': 4}

定義完init()方法後,創建的每個實例都有自己的屬性,也方便直接調用類中的函數。

最後一個要說的就是__init__,__new__執行順序的探索

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章