py魔法方法(__new__/metaclass元類、__init__、__getattr__、__callable__等)

"""
py魔法方法.py
(__new__/metaclass元類、__init__、__getattr__、__callable__等)

使用:

1、#使用__new__() 來定義創建對象的方式。
2、#使用__init__() 來初始化創建的實例對象。
3、#使用:__str__()魔法方法來定義print輸出
4、#使用:__repr__()魔法方法來定義調試輸出,即非print輸出
5、#使用:__iter__及__next__魔法方法來定義 對象的迭代循環。
6、#使用:__getitem__來定義迭代對象的指定索引,即列表的索引
#注意:__getitem__通過實例對象的輸入參數類型是否slice,來實現切片操作。

7、#使用:__getattr__魔法方法 動態返回一個屬性
#注意:__getattr__只有在沒找到屬性的情況下才調用,已有的屬性不會在__getattr__中查找。

8、#使用:__call__魔法方法 來使 類的實例對象可以直接實例對象()調用。即:實例對象() 的格式進行輸出
#對實例對象進行直接調用就好比對一個函數進行調用一樣,可以定義參數等。
#注意:實例對象調用 和 函數調用的概念區別,實質一樣。

#使用:callable()函數來判斷一個對象是否是 可調用對象,即:是否能被調用()。


"""


#################### 定製類:魔法方法/特殊屬性方法
#使用:特殊屬性和特殊方法(__xxx__格式的屬性方法)可以在類定義中進行定製類操作。
#定製類官方參考網址:https://docs.python.org/zh-cn/3/reference/datamodel.html#special-method-names


#__new__、__init__(創建對象、初始化對象)
#構造方法包括創建對象和初始化對象,在python當中,分爲兩步執行:先執行__new__方法,然後執行__init__方法;

##########
#使用__new__() 來定義創建對象的方式。
#__new__  在實例創建之前被調用的,因爲它的任務就是創建實例然後返回該實例,是個靜態方法。

#使用:metaclass控制類的創建行爲,根據metaclass創建出類,即:先定義metaclass,就可以創建類,最後創建實例。
#metaclass允許創建類或者修改類,也可以把類看成是metaclass創建出來的“實例”。
#注意:默認metaclass的類名總是以Metaclass結尾,以便清楚地表示這是一個metaclass
#注意:metaclass是類的模板,所以必須從`type`類型派生:
class ListMetaclass(type):
    """定義一個類,用來作爲後期創建類的元類。"""
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)           #返回type()創建類的__new__方法。
#####
#使用:__new__魔法方法定義類創建過程。
#__new__()方法接收到的參數依次是:
#參數1:當前準備創建的類的對象;
#參數2:類的名字;
#參數3:類繼承的父類集合;
#參數4:類的方法集合。


##########
#使用__init__() 來初始化創建的實例對象。
#__init__ 當實例對象創建完成後被調用的,然後設置對象屬性的一些初始值。

#####
#使用:super函數 調用父類方法。
#使用:super(子類名,self).__init__() 來顯示調用 父類初始化方法
#注意:Python3可以使用直接使用 super().xxx 代替 super(Class, self).xxx :


#子類沒有定義自己的初始化函數,父類的初始化函數會被默認調用:
#定義父類:Parent
class Parent(object):
    def __init__(self, name):
        self.name = name
        print("create an instance of:", self.__class__.__name__)
        print("name attribute is:", self.name)
#定義子類Child ,繼承父類Parent       
class Child(Parent):
    pass
#子類實例化時,由於子類沒有初始化,此時父類的初始化函數就會默認被調用
#且必須傳入父類的參數name
c = Child("init Child") 


#子類定義了自己的初始化函數,而在子類中沒有顯示調用父類的初始化函數,則父類的屬性不會被初始化
class Child(Parent):
    #子類中沒有顯示調用父類的初始化函數,相當於重寫了初始化方法。
    def __init__(self):
        print("call __init__ from Child class")
c = Child("init Child") 
print()  


#子類定義了自己的初始化函數,顯示調用父類,子類和父類的屬性都會被初始化
class Child(Parent):
    def __init__(self):
        print('Call __init__ from Child class')
        super(Child,self).__init__('data from Child') #要將子類Child和self傳遞進去
d=Parent('Tom')
c=Child()
print(c.name)


##########
#__str__(定義print輸出)、__repr__(定義調試輸出)
#使用:__str__()魔法方法來定義print輸出
#使用:__repr__()魔法方法來定義調試輸出,即非print輸出
class Student(object):
    def __init__(self,name):
        self.name=name
    def __str__(self):
        """ #使用:__str__()魔法方法來定義print輸出"""
        return 'print輸出內容:\nStudent Object (name:{})'.format(self.name)
    """將輸出設置一致"""
#    __repr__=__str__
    def __repr__(self):
        """#使用:__repr__()魔法方法來定義調試輸出,即非print輸出"""
        return '調試輸出內容: \nStudent Object  (name:{})'.format(self.name)
print(Student('Michael'))
Student('Michael')


##########
#__iter__、__next__(定義循環可迭代對象)
#使用:__iter__及__next__魔法方法來定義 對象的迭代循環。

#定義斐波那契數列類
class Fib(object):
    def __init__(self):
        self.a, self.b=0,1
    def __iter__(self):
        "定義迭代返回值,如果返回None則表示不可迭代"
        return self        #實例本身就是迭代對象,所有返回自己本身
    def __next__(self):
        self.a, self.b=self.b, self.a+self.b
        if self.a>1000:
            raise StopIteration()
        return self.a
Fib()
for f in Fib():
    print(f)


##########
#__getitem__(定義迭代的指定索引)
#使用:__getitem__來定義迭代對象的指定索引,即列表的索引
#注意:__getitem__通過實例對象的輸入參數類型是否slice,來實現切片操作。


#使用:__getitem__來定義迭代對象的指定索引,即列表的索引
class Fib(object):
    def __getitem__(self,n):
        a,b=1,1
        for x in range(n):
            a,b=b,a+b
        return a
f=Fib()
f[5]     #像list那樣按照下標取出元素

#注意:__getitem__通過實例對象的輸入參數類型是否slice,來實現切片操作。
#定義迭代對象的 切片操作 slice
class Fib(object):
    def __getitem__(self,n):
        """如果輸入參數屬於 int整數類型,則輸出該索引對應的值"""
        if isinstance(n,int):
            a,b=1,1
            for x in range(n):
                a,b=b,a+b
            return a
        """通過對 實例對象的參數判斷 來實現切片操作;
        即:如果輸入參數屬於 slice,則定義並返回新的列表"""
        if isinstance(n,slice):
            start=n.start
            stop=n.stop
            if start is None:
                start=0
                
            a,b=1,1
            L=[]
            for x in range(stop):
                if x >= start:
                    L.append(a)    #通過不斷的添加a來形成返回結果用的L列表,即切片的結果。
                a,b=b,a+b
            return L
f=Fib()
f[1:6]



##########
#__getattr__
#使用:__getattr__魔法方法 動態返回一個屬性
#注意:__getattr__只有在沒找到屬性的情況下才調用,已有的屬性不會在__getattr__中查找。
class Student(object):
    def __init__(self):
        self.name='Michael'
    """
    #使用:__getattr__魔法方法 動態返回一個屬性;
    #注意:__getattr__只有在沒找到屬性的情況下才調用,已有的屬性不會在__getattr__中查找。
    """
    def __getattr__(self,attr):
        if attr == 'score':
            return 99        
s=Student()
s.name
s.score

class Student(object):
    def __getattr__(self,attr):
        if attr == 'age':
            return lambda:25      #返回函數也是完全可以的。只是需要注意調用的時候加上括號()
        return AttributeError('\'Stuende類對象沒有屬性{}'.format(attr))
s=Student()
s.age()                           #注意調用的時候加上括號,因爲__getattr__裏定義返回的是函數。
s.time
"""沒有定義__call__魔法方法的類不能直接通過類實例對象名()如函數般來調用"""
#s().time                          


#通過重寫類的__getattr__ 及 __str__方法實現鏈式調用Api輸出。
class Chain(object):
    def __init__(self,path=''):
        self._path=path
    def __getattr__(self,path):
        return Chain('{}/{}'.format(self._path, path))
    def __str__(self):
        return self._path
    __repr__=__str__
Chain().status
Chain().users.ker945.git


##########
#__call__:實例對象()調用
#使用:__call__魔法方法 來使 類的實例對象可以直接實例對象()調用。即:實例對象() 的格式進行輸出
#對實例對象進行直接調用就好比對一個函數進行調用一樣,可以定義參數等。
#注意:實例對象調用 和 函數調用的概念區別,實質一樣。

#使用:callable()函數來判斷一個對象是否是 可調用對象,即:是否能被調用()。

class Student(object):
    def __init__(self,name):
        self.name=name
    def __call__(self,age=10):
        print('My name is {},My age is {}'.format(self.name,age))
s=Student('Michael')
"""定義__call__魔法方法的類,可以直接通過類實例對象名()如函數般來調用,否則不能直接類實例對象名()調用"""
s(11)


"""如果__call__類對象如函數般調用,則就模糊了對象和函數的界限;則通過callable()函數來判斷對象是否可調用"""
callable(Student('xiaowang'))
callable(max)
callable(1)
callable('str')





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