面向對象+
靜態方法和類方法
靜態方法
導入
通過裝飾器@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
: 屬性的設置方法fde
l:屬性的刪除方法- 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