Python學習(八)---- 面向對象類之進階

原文地址: https://blog.csdn.net/fgf00/article/details/52479307 編輯:智能算法,歡迎關注! 上期我們一起學習了python中的類

Python學習(七)---- 面向對象學習(類)

今天我們繼續學習python中類。

目錄

1 類的高級方法

2 類的特殊成員方法

3 反射

4 異常處理

1 類的高級方法

1.1 靜態方法(@staticmethod)

通過@staticmethod裝飾器即可把其裝飾的方法變爲一個靜態方法。普通的方法,可以在實例化後直接調用,並且在方法裏可以通過self.調用實例變量或類變量,但靜態方法是不可以訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實相當於跟類本身已經沒什麼關係了,它與類唯一的關聯就是需要通過類名來調用這個方法

1class Dog(object):
2    def __init__(self,name):
3        self.name = name
4    @staticmethod  # 把eat方法變爲靜態方法
5    def eat(self):
6        print("%s is eating" %self.name)
7
8d = Dog("wangcai")
9d.eat()

上面的調用會出以下錯誤,

1TypeError: eat() missing 1 required positional argument: 'self'

說是eat需要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法後,再通過實例調用時就不會自動把實例本身當作一個參數傳給self了。 想讓上面的代碼可以正常工作有兩種辦法

  • 調用時主動傳遞實例本身給eat方法,即d.eat(d)
  • 在eat方法中去掉self參數,但這也意味着,在eat中不能通過self. 調用實例中的其它變量了
1class Dog(object):
2    def __init__(self,name):
3        self.name = name
4    @staticmethod  # 把eat方法變爲靜態方法
5    def eat(self):
6        print("%s is eating" %self.name)
7
8d = Dog("wangcai")
9d.eat(d)

作用:只是相當於一個單純函數,要傳參數,就要把實例傳進去。 如果說和類有關係,就是必須有類名去調用。調用不了類或實例中的任何屬性

1.2 類方法(@classmethod)

類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是: 類方法只能訪問類變量,不能訪問實例變量

1class Dog(object):
2    def __init__(self,name):
3        self.name = name
4    @classmethod
5    def eat(self):
6        print("%s is eating" % self.name)
7
8d = Dog("wangcai")
9d.eat()

執行報錯如下,說Dog沒有name屬性,因爲name是個實例變量,類方法是不能訪問實例變量的

1AttributeError: type object 'Dog' has no attribute 'name'

此時可以定義一個類變量,也叫name,看下執行效果

 1class Dog(object):
 2    name = "哮天犬"
 3    def __init__(self,name):
 4        self.name = name
 5    @classmethod
 6    def eat(self):
 7        print("%s is eating" % self.name)
 8
 9d = Dog("wangcai")
10d.eat()

1.3 屬性方法(@property)

屬性方法的作用就是通過@property把一個方法變成一個靜態屬性 (函數–>變量)

1class Dog(object):
2    def __init__(self,name):
3        self.name = name
4    @property
5    def eat(self):
6        print("%s is eating" % self.name)
7
8d = Dog("wangcai")
9d.eat()

調用會出以下錯誤, 說NoneType is not callable, 因爲eat此時已經變成一個靜態屬性了,不是方法了, 想調用已經不需要加()號了,直接d.eat就可以了

1TypeError: 'NoneType' object is not callable

正常調用如下

1d.eat
2# 輸出
3wangcai is eating

但是有個問題,如果eat有其他參數,沒法傳參數。而且即使變成了靜態屬性,也沒法像普通變量那樣“=”賦值 怎麼傳參數呢?

  • 屬性方法賦值
 1class Dog(object):
 2    def __init__(self,name):
 3        self.name = name
 4        self.__food = None
 5    @property
 6    def eat(self):
 7        print("%s is eating %s" %(self.name, self.__food))
 8    @eat.setter  # 賦值調用屬性,調這個方法
 9    def eat(self,food):
10        print("set to food:",food)
11        self.__food = food
12
13d = Dog("wangcai")
14d.eat
15d.eat = "baozi"
16d.eat

如果傳多個參數,‘d.eat = “baozi1”,”baozi2”’,接收爲元組形式。

  • 刪除屬性方呢 執行del刪除
1del d.eat

報錯:

1AttributeError: can't delete attribute

默認不能刪除,要刪除也是在類裏再寫一個方法

 1class Dog(object):
 2    def __init__(self,name):
 3        self.name = name
 4        self.__food = None
 5    @property
 6    def eat(self):
 7        print("%s is eating %s" %(self.name, self.__food))
 8    @eat.setter  # 賦值調用屬性,調這個方法
 9    def eat(self,food):
10        print("set to food:",food)
11        self.__food = food
12    @eat.deleter  # 刪除屬性
13    def eat(self):
14        del self.__food
15        print("Delete the finished")
16
17d = Dog("wangcai")
18d.eat
19d.eat = "baozi"
20d.eat  # 傳完參數後調用
21del d.eat
22d.eat  # 刪完後調用

報錯如下,說明已經刪除了

1AttributeError: 'Dog' object has no attribute '_Dog__food'

好吧,把一個方法變成靜態屬性有什麼卵用呢?既然想要靜態變量,那直接定義成一個靜態變量不就得了麼?well, 以後你會需到很多場景是不能簡單通過 定義 靜態屬性來實現的, 比如 ,你想知道一個航班當前的狀態,是到達了、延遲了、取消了、還是已經飛走了, 想知道這種狀態你必須經歷以下幾步:

  • 連接航空公司API查詢
  • 對查詢結果進行解析
  • 返回結果給你的用戶

因此這個status屬性的值是一系列動作後纔得到的結果,所以你每次調用時,其實它都要經過一系列的動作才返回你結果,但這些動作過程不需要用戶關心, 用戶只需要調用這個屬性就可以.

2 類的特殊成員方法

2.1 __doc__ 表示類的描述信息

1class Foo:
2    """ 描述類信息 """
3    def func(self):
4        pass
5
6print Foo.__doc__

2.2 __module__和__class__

__module__ 表示當前操作的對象在那個模塊 __class__ 表示當前操作的對象的類是什麼 aa.py

1class C:
2
3    def __init__(self):
4        self.name = 'fgf'

index.py

1from aa import C
2
3obj = C()
4print obj.__module__   # 輸出 aa,即:輸出模塊
5print obj.__class__    # 輸出 aa.C,即:輸出類

2.3 __init__ 構造方法

通過類創建對象時,自動觸發執行。

2.4 __del__ 析構方法

當對象在內存中被釋放時,自動觸發執行。

2.5 __call__方法

對象後面加括號,觸發執行。 注:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

1class Foo:
2    def __init__(self):
3        pass
4
5    def __call__(self, *args, **kwargs):
6        print ('__call__',args,kwargs)
7
8obj = Foo()         # 執行 __init__
9obj(1,2,3,a=4,b=5)  # 執行 __call__

2.6 __dict__ 方法

查看類或對象中的所有成員

 1class Province:
 2    country = 'China'
 3    def __init__(self, name, count):
 4        self.name = name
 5        self.count = count
 6    def func(self, *args, **kwargs):
 7        print ('func')
 8
 9# 獲取類的成員,即:靜態字段、方法、
10print (Province.__dict__)
11# 輸出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
12
13obj1 = Province('HeBei',10000)
14# 獲取 對象obj1 的成員
15print (obj1.__dict__)
16# 輸出:{'count': 10000, 'name': 'HeBei'}

2.7 __str__ 方法

如果一個類中定義了str方法,那麼在打印對象 時,默認輸出該方法的返回值。

1class Foo:
2    def __str__(self):
3        return 'fgf'
4
5obj = Foo()
6print (obj)

定以後,輸入定義的值,不定義默認返回對象地址“<main.Foo object at 0x0000027BF421E9E8>”

2.8 __getitem__、__setitem__、__delitem__

用於索引操作,如字典。以上分別表示獲取、設置、刪除數據

 1class Foo(object):
 2    def __init__(self):
 3        self.data = {}
 4    def __getitem__(self, key):
 5        print('__getitem__',key)
 6        return self.data.get(key)
 7    def __setitem__(self, key, value):
 8        print('__setitem__',key,value)
 9        self.data[key] = value
10    def __delitem__(self, key):
11        print('__delitem__',key)
12
13obj = Foo()
14
15obj['name'] = 'fgf'   # 設置,自動觸發執行 __setitem__
16print(obj.data)
17print(obj['name'])     # 獲取值,自動觸發執行 __getitem__
18del obj['name']        # 觸發__delitem__,只是調用那個方法,具體刪不刪看自己配置

2.9 類的起源 __new__和 __metaclass__

1class Foo(object):
2    def __init__(self,name):
3        self.name = name
4f = Foo("fgf")

上述代碼中,f 是通過 Foo類實例化的對象,其實,不僅f是一個對象,Foo類本身也是一個對象,因爲在Python中一切事物都是對象。 如果按照一切事物都是對象的理論:obj對象是通過執行Foo類的構造方法創建,那麼Foo類對象應該也是通過執行某個類的 構造方法 創建。

1print type(f) # 輸出:<class '__main__.Foo'>     表示,obj 對象由Foo類創建
2print type(Foo) # 輸出:<type 'type'>              表示,Foo類對象由 type 類創建

所以,f對象是Foo類的一個實例,Foo類對象是 type 類的一個實例,即:Foo類對象 是通過type類的構造方法創建。 那麼,創建類就可以有兩種方式:

  • 普通方式(也是通過type創建的類,只是已經封裝好了)
1class Foo(object):
2
3    def func(self):
4        print 'hello fgf'
  • 特殊方式
1def func(self):
2    print('hello fgf')
3
4Foo = type('Foo',(object,), {'talk': func})
5# type第一個參數:類名
6# type第二個參數:當前類的基類
7# type第三個參數:類的成員
8f = Foo()
9f.talk()

f是Foo的對象,Foo又是type的對象,所以type又稱類的類 加上構造函數

 1def func(self):
 2    print("hello %s %s"%(self.name,self.age))
 3
 4def __init__(self,name,age):
 5    self.name = name
 6    self.age = age
 7Foo = type('Foo',(object,),{'func':func,
 8                            '__init__':__init__})
 9
10f = Foo("fgf",22)
11f.func()

So ,記住,類 是由 type類實例化產生,那麼問題來了,類默認是由type類實例化產生,type類中如何實現的創建類?類又是如何創建對象? 答:類中有一個屬性 __metaclass__,其用來表示該類由 誰來實例化創建,所以,我們可以爲 __metaclass__ 設置一個type類的派生類,從而查看類創建的過程。 以下代碼python2裏運行看效果

 1class MyType(type):
 2    def __init__(self, what, bases=None, dict=None):
 3        print("--MyType init---")
 4        super(MyType, self).__init__(what, bases, dict)
 5    def __call__(self, *args, **kwargs):
 6        print("--MyType call---")
 7        obj = self.__new__(self, *args, **kwargs)
 8        self.__init__(obj, *args, **kwargs)
 9
10class Foo(object):
11    __metaclass__ = MyType
12    def __init__(self, name):
13        self.name = name
14        print("Foo ---init__")
15    def __new__(cls, *args, **kwargs):
16        # __new__是用來創建實例的,定製類,先運行new裏調用init,這裏寫,對默認的重構
17        print("Foo --new--")
18        # print(object.__new__(cls))
19        return object.__new__(cls)  # 返回給init,cls這代表Foo,相當於對象的self
20        # 調用父類的__new__方法
21
22# 第一階段:解釋器從上到下執行代碼創建Foo類
23# 第二階段:通過Foo類創建obj對象
24obj = Foo("Fgf")

(默認之前應該還有個myType.new)先執行myType.init,再執行myType.call,再執行Foo.new,最後Foo.init

3 反射

如果用戶輸入信息如”fgf”,通過輸入字符串”fgf”去調用實例的屬性,怎麼實現。 不可能”name=input()”,再用name去調用fgf屬性,那樣調用的是name而不是fgf。 要想把用戶輸入字符串轉爲一個變量名,而不是一個值就需要用到: 反射(實現用戶輸入字符串爲類的方法) 通過字符串映射或修改程序運行時的狀態、屬性、方法, 有以下4個方法 attr –> attribute [əˈtrɪbjut] 屬性; (人或物的) 特徵

  • hasattr(obj,name_str)

判斷object中有沒有一個name字符串對應的方法或屬性

 1class Foo(object):
 2    # name = ''
 3    def __int__(self):
 4        self.name = 'fgf'
 5    def func(self):
 6        return "func"
 7
 8obj = Foo()
 9
10print(hasattr(obj, 'name'))  # 輸出False
11print(hasattr(obj, 'func'))  # 輸出True
  • getattr(obj,name_str)

根據字符串去獲取obj對應方法的內存地址

1def getattr(object, name, default=None):
2    """
3    getattr(object, name[, default]) -> value
4    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
5    """
 1class Foo(object):
 2    def __init__(self):
 3        self.name = 'fgf'
 4    def func(self):
 5        print(self.name,'say Hi')
 6        return "func"
 7
 8obj = Foo()
 9print(getattr(obj,'func'))
10getattr(obj, 'func')()  # # same as: obj.func()
  • setattr(obj,name_str,func)

動態把一個函數裝到類裏面

1def setattr(x, y, v): # real signature unknown; restored from __doc__
2    """
3    setattr(x, 'y', v) is equivalent to ``x.y = v''
4    """
 1def func1(self):
 2    print(self.name,'say Hi')
 3    return "func"
 4
 5class Foo(object):
 6    def __init__(self):
 7        self.name = 'fgf'
 8    def func2(self):
 9        print(self.name,'say fu')
10        return "func"
11obj = Foo()
12
13choice = input("請輸入調用方法名[func1|func2]:")
14if hasattr(obj,choice):  # 實例中有這個方法,執行實例中的方法
15    print(getattr(obj,choice))
16else:                    # 動態加載函數封裝到類中
17    setattr(obj,choice,func1)
18    func = getattr(obj,choice)
19    func(obj)
  • delattr()

刪除set添加的屬性

1def delattr(x, y): # real signature unknown; restored from __doc__
2    """
3    delattr(x, 'y') is equivalent to ``del x.y''
4    """
 1class Foo(object):
 2    def __init__(self):
 3        self.name = 'fgf'
 4    def func(self):
 5        print(self.name,'say Hi')
 6
 7obj = Foo()
 8
 9setattr(obj, 'age', 18)
10print(obj.name,obj.age)
11# delattr(obj, 'func')
12delattr(obj,'age')  # 只能刪除setattr動態添加的,默認的不可以刪除
13print(obj.name,obj.age)

動態導入模塊

1import importlib
2
3__import__('import_lib.metaclass') #這是解釋器自己內部用的
4#importlib.import_module('import_lib.metaclass') #與上面這句效果一樣,官方建議用這個

isinstance(obj, cls):檢查是否obj是否是類 cls 的對象 issubclass(sub, super):檢查sub類是否是 super 類的派生類

4 異常處理

在編程過程中爲了增加友好性,在程序出現bug時一般不會將錯誤信息顯示給用戶,而是現實一個提示的頁面

 1try:
 2    diction = {}
 3    diction[1]
 4    names = []
 5    names[2]
 6except IndexError as e:             # python3.x 裏不是逗號,都是as
 7    print(e)
 8except (KeyError,IndexError) as e:  # 採用統一處理辦法
 9    print(e)
10except Exception as e:              # 抓住所有錯誤
11    print(e)
12else:                               # 沒出錯執行這個
13    pass
14finally:                            # 不管有沒有錯,都執行
15    pass
16
17try:
18    status = 1
19    if status != 0 :
20        raise Exception("自定義異常")
21except Exception as e:
22    print(e)

聲明:本文系網絡轉載,版權歸原作者所有。如涉及版權,請聯繫刪除!

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