面向對象,類中的各種方法

面向對象+

靜態方法和類方法

靜態方法

導入

通過裝飾器@staticmethod來進行裝飾。靜態方法既不需要傳遞類對象也不需要傳遞實例對象

靜態方法也可以通過實例對象和類對象去訪問

class Dog:
    type='狗'
    def __init__(self):
        name=None
    #靜態方法
    @staticmethod
    def introduce():#靜態方法不會自動傳遞實例對象和類對象
        print('犬科哺乳動物')
dog=Dog()#實例化
Dog.introduce()
dog.introduce()
#結果
犬科哺乳動物
犬科哺乳動物

如果不添加@staticmethod,由於introduce沒有傳入self,會報錯。所有靜態方法是類中的函數,不需要實例。

靜態方法主要是用來存放邏輯性的代碼,邏輯上屬於類,但和類本身沒有關係。也就是說,在靜態方法中,

不會涉及類中屬性和方法的操作。

即:靜態方法是一個獨立的,單純的函數,僅僅是託管與某個類的名稱空間中,便於維護和管理

使用場景
  • 當方法中 既不需要使用實例對象(如實例對象,實例屬性),也不需要使用類對象 (如類屬性、類方法、創建實例等)時,定義靜態方法
  • 取消不需要的參數傳遞,有利於 減少不必要的內存佔用和性能消耗
  • 如果在類外面寫一個同樣的函數來做這些事,打亂了邏輯關係,導致代碼維護困難,使用靜態方法。

類方法

解釋
  • 類對象所擁有的方法
  • 需要用修飾器@classmethod來標識其爲類方法
  • 對於類方法,第一個參數必須是類對象,一般以cls作爲第一個參數
class Dog:
    __type='狗'
    #類方法,用class來進行修飾
    @classmethod
    def get_type(cls):
        return cls.__type
print(Dog.get_type())
#結果
使用場景

當方法中需要使用類對象(如訪問私有類屬性),定義類方法

類方法一般如類屬性配合使用

注意

類中定義了同名的對象方法,類方法以及靜態方法時,調用方法會優先執行最後定義的方法

class Dog:
    def demo_method(self):
        print('對象方法')
    @classmethod
    def demo_method(cls):
        print('類方法')
    @staticmethod
    def demo_method(): #最後定義,優先調用
        print('靜態方法')
dog=Dog()
Dog.demo_method()
dog.demo_method()
#結果
靜態方法
靜態方法

property

引入

在python中主要爲屬性提供一個便利的操作方式。

如果我們現在需要設計一個銀行賬戶類,這個類中包含賬號人的姓名,餘額。

不考慮具體的接口

class Account(object):
    def __init__(self,name,money):
        self.name=name
        self.money=money

存在的問題:不安全(雖然設計方便,但是所有屬性可以外部訪問修改)

改進1:隱藏細節,使用私有屬性__.屬性名

對於賬號信息而言,金額不允許讓用戶直接修改。(如果修改,只能去窗口辦理)

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money

代碼改進後,確實能夠增加安全性,讓外部無法訪問及修改。但是此時,如果是合法操作的訪問和修改

(銀行窗口),也無法完成,需要添加相應方法去訪問。

**改進2:**添加相應的方法

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money
    def get_name(self):
        return self.__name
    def set_balance(self,money):
        self.__money=money
    def get_balance(self):
        return self.__money

改進3:保證數據的有效性

如果self.__money輸入不合法(如輸入一個字符串abc)

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money
    def get_name(self):
        return self.__name
    def set_balance(self,money):
        if isinstance(money,int):
            if money>0 :
                self.__money=money
            else:
                raise ValueError('輸入金額不正確')
        else:
            raise ValueError('輸入的數據類型不對')
    def get_balance(self):
        return self.__money

經過幾次更改,程序更加實用,安全性也越來越高,但是複雜度也在增加

實例

在python中,提供一個property類,通過對創建這個類的對象的設置,在使用對象的私有屬性時,可以

不再使用屬性的函數的調用方式,而是像普通的公有屬性一樣取使用,方便開發人員使用。

property(fget=None,fset=None, fdel=None, doc=None)

  • fget:屬性的獲取方法
  • fset: 屬性的設置方法
  • fdel:屬性的刪除方法
  • doc:屬性描述

property是一個類,__init__方法由四個參數組成,實例後返回一個用來操作屬性的對象

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money
    def __get_name(self):
        return self.__name
    def set_balance(self,money):
        if isinstance(money,int):
            if money>0 :
                self.__money=money
            else:
                raise ValueError('輸入金額不正確')
        else:
            raise ValueError('輸入的數據類型不對')
    def get_balance(self):
        return self.__money
    #設置property類來爲屬性設置便利的訪問方式
    name=property(__get_name)
    balance=property(get_balance,set_balance)
ac=Account('bob',18000)
print(ac.name)
print(ac.balance)
ac.balance=28000
print(ac.balance)
#結果:
bob
18000
28000

基本形式

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")
c=C()
print(c.x)
c.x=1    
print(c.x)
del c.x
print(c.x)
#結果
None #未設置值,直接讀取初始值爲None
Traceback (most recent call last):
1    #完成賦值,讀取新的值爲1
  File "D:/text.py", line 46, in <module> #刪除後該值不存在,報錯
    print(c.x)
  File "D:/text.py", line 32, in getx
    return self._x
AttributeError: 'C' object has no attribute '_x'
使用裝飾器
class C:
    def __init__(self):
        self._x = None
    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x
    @x.setter
    def x(self, value):
        self._x = value
    @x.deleter
    def x(self):
        del self._x
c=C()
print(c.x)
c.x=1
print(c.x)
del c.x
print(c.x)      
#結果與上面的完全相同
總結

property最大的好處就是在類中把一個方法變成屬性調用,起到既能檢查屬性,還能用屬性的方式來訪問該屬性的作用。進行不同的操作(刪改查)我們可以不需要記相應的方法的名稱,而是直接對x進行操作

self

引入

如果對象的方法中需要使用該對象的屬性,怎麼處理

關鍵字self主要用於對象方法中,表示調用該方法的對象。

在方法中使用self,可以獲取到調用當前方法的對象,進而獲取該對象的屬性和方法。

調用對象的方法時,某個對象調用其方法時,python解釋器會把這個對象作爲第一個參數傳遞給方法,所有,開發者只需要在定義的時候預留第一個參數self即可。

class Cat:
    #方法
    def introduce(self):
        print('aaa')
cat=Cat()			#實例化
cat.introduce()		#能夠輸出	
Cat.introduce()		#self未捕捉,將其視爲普通的函數,直接報錯沒有傳入位置參數

使用self操作屬性和對象的變量名在效果上類似。如果屬性在賦值時還沒有被定義。就會自動定義一個屬性並賦值。

補充三種方法

__new__方法

創建對象時,系統會自動調用__new__方法。

開發者可以使用__new__方法自定義對象的創建過程。

__new__至少要有一個參數cls,代表要實例化的類,此參數在實例化時由Python解釋器自動提供
__new__必須要有返回值,返回實例化出來的實例,這點在自己實現__new__時要特別注意,可以return父類__new__出來的實例,或者直接是object的__new__出來的實例
__init__有一個參數self,就是這個__new__返回的實例,__init____new__的基礎上可以完成一些其它初始化的動作,__init__不需要返回值
- 如果創建對象時傳遞了自定義參數,且重寫了new方法,則new也必須 “預留” 該形參,否則__init__方法將無法獲取到該參數

class Cat:
    def __new__(cls, name):
        print('創建對象')
        return object.__new__(cls)
    def __init__(self,name):
        print('對象初始化')
        self.name=name
    def __str__(self):
        return  '%s' % self.name
cat=Cat('貓')
print(cat)
#結果:
創建對象
對象初始化
貓
實例
class A(object):
    def __new__(cls, x):
        print ('this is in A.__new__, and x is ', x)
        return super(A, cls).__new__(cls)

    def __init__(self, y):
        print ('this is in A.__init__, and y is ', y)
class B(A):
    def __new__(cls, z):
        print ('this is in B.__new__, and z is ', z)
        return A.__new__(cls, z)

    def __init__(self, m):
        print ('this is in B.__init__, and m is ', m)
a = A(100)
print ('=' * 20)
b = B(200)
print (type(b))
#結果
this is in A.__new__, and x is  100
this is in A.__init__, and y is  100
====================
this is in B.__new__, and z is  200
this is in A.__new__, and x is  200
this is in B.__init__, and m is  200
<class '__main__.B'>

註釋

1.定義A類作爲下面類的父類,A類繼承object類,因爲需要重寫A類的__new__()函數,所以需要繼承object基類,成爲新式類,經典類沒有__new__()函數;

2.子類在重寫__new__()函數時,寫return時必須返回有繼承關係的類的__new__()函數調用,即上面代碼中的B類繼承自A類,則重寫B類的__new__()函數,寫return時,只能返回A.__new__(cls)或者object.__new__(cls),不能返回C類的;

3.由註釋掉的代碼執行結果可以看出,B類雖然繼承自A類,但是如果沒有重寫B類的__new__()函數,則默認繼承的仍是object基類的__new__(),而不是A的;

4.B類的__new__()函數會在B類實例化時被調用,自動執行其中的代碼語句,但是重寫__new__()函數不會影響類的實例化結果,也就是說不管寫return時返回的是A的還是object的,B類的實例化對象就是B類的,而不會成爲A類的實例化對象;只是在實例化時,如果返回的是A.__new__(cls),則會執行A類中定義的__new__()函數;

5.new()函數確定了類的參數的個數,object類默認定義的__new__()函數的參數爲(cls, *args),但如果在子類中重寫了__new__(cls, x), 則實例化類時,需要傳入一個x參數,而__init__()函數接受到的有兩個參數,一個是實例化生成的實例對象self代替,一個是傳入的實參x的值;

特性

__new__()方法是在類準備將自身實例化時調用。__new__()方法始終都是類的靜態方法,即使沒有被加上靜態方法裝飾器。是因爲無論怎樣重寫類的__new__()函數,追溯到源頭都是繼承自object的__new__()函數,而object類中定義的__new__()函數就被定義成了靜態函數,被@stacitmethod修飾

__call__方法

對象後面加括號,觸發執行

構造方法的執行是由創建對象觸發的,即:對象=類名( )

而對於,__call__方法的執行是由對象後加括號觸發的,即對象( )或者類( )

class A(object):
    def __call__(self, x):
        print ('__call__ called, print x: ', x)    
a = A()   #與__init__不同,在實例化時不需要傳參
a('123')  #此時直接給實例對象a傳參

__call__ called, print x:  123

看a(‘123’)這是函數的調用方法,這裏a實際上是類對象A的實例對象,實例對象能想函數一樣傳參並被調用,就是__call__()方法的功能;

__doc__方法

類的註釋,無法繼承給子類的

class foo():
    '''這是一個類:foo'''
    pass
class bar(foo):
    pass
print(foo.__doc__)
print(bar.__doc__)
#結果
這是一個類:foo
None

綜合上述方法實例

class A(object):
    def __init__(self, x):
        print('x in __init__', x)
    def __new__(cls, y):
        print('y in __new__', y)
        return super(A, cls).__new__(cls)
    def __call__(self, z):
        print('z in __call__', z)
A('123')('abc')
#結果
y in __new__ 123 	 #new中獲取傳入的參數
x in __init__ 123	 #init構造傳入的參數形成實例	
z in __call__ abc    #A('123')現當於一個實例a,a將abc傳給call
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章