Python/類
概述
編輯 class ClassName(Base1, Base2, Base3):
"""Class documentation"""
def __init__(self,參數):
語句
def funcName(self,參數): #self 不是 python 關鍵字,把它換成其它名字也是表示類實例
語句
localVar = value0
self.DataMember = value1 # instance attribute
self.__privateDataMember2 = value2 # instance private attribute
dataMember1 = value # A class attribute, not an instance one
<statement-N>
數據成員可分為
- 類的變量(類的屬性 class attribute):類定義內部、但在成員函數之外 賦值的變量。可以按照 className.VarName或者instanceName.VarName讀寫,但是用instanceName.VarName寫入值的時候,實際上是自動作為實例的變量寫值;實例變量會屏蔽對同名類變量的訪問。
- 實例的變量(實例的屬性 instance attribute):類的成員函數內部用self.VarName=Value 這種方式賦值的變量。不能被其它實例讀寫
- 本地變量:類的成員函數內部用VarName=Value 這種方式賦值的變量。盡在當前函數內部有效。
方法成員可分為:
- 實例方法:一般第一個參數是self
- 類方法:通常將第一個參數命名為cls。方法調用時會自動把類本身綁定給 cls 參數。類方法需要使用@classmethod修飾符進行修飾。類方法推薦使用類名直接調用。classmethod 主要用於操作類級別的數據或創建類的實例。它可以訪問和修改類的屬性,而不能直接訪問實例的屬性。
- 靜態方法:使用@staticmethod修飾。沒有隱式的第一個參數(如 self 或 cls)。它不訪問類的屬性或實例的屬性。靜態方法更像是一個普通的函數,只是定義在類的命名空間下。不能訪問類或實例的屬性或方法。通過類或實例調用 staticmethod。
在類的方法之外的代碼,會在類定義時直接執行。例如一行print語句,就會直接屏幕輸出。
封裝功能是把一些數據與方法限於類的內部使用,不對外暴露。Python通過在變量名字與方法名字前加入2個下劃線實現。
類的內部實現
編輯類的字典(Class Dictionar)用vars(ClassName)或ClassName.__dict__訪問,其中包含了:
- 類的變量名與值
- 類的方法名與地址
- __module__與對應值
- __dict__
- __weakref__
- __doc__與字符串值
實例的字典用用vars(InstanceName)或InstanceName.__dict__訪問,其中包含了實例變量的名字與值。
在外部訪問一個不存在的實例屬性,這自動增加這個實例屬性。用del關鍵字可以刪除實例屬性。這叫做動態類結構(Dynamic Class Structure)
類的繼承
編輯如果子類方法遮蔽了父類的同名方法,在子類中調用父類的被遮蔽的方法的方式有2種,分別是:
- 類可以看做一個獨立空間,在類的外部調用其中的實例方法,可以向調用普通函數那樣,只不過需要額外備註類名(此方式又稱為未綁定方法);
- super() 函數用於調用父類實例的方法。但如果涉及多繼承,該函數只能調用第一個直接父類實例的方法。
如果在子類中定義構造方法,則必須在該方法中調用父類的構造方法。
class People:
def __init__(self,name):
self.name = name
def say(self):
print("我是人,名字为:",self.name)
class Animal:
def __init__(self,food):
self.food = food
def display(self):
print("我是动物,我吃",self.food)
class Person(People, Animal):
#自定义构造方法
def __init__(self,name,food):
#调用 People 类的构造方法
super().__init__(name)
#super(Person,self).__init__(name) #执行效果和上一行相同
#People.__init__(self,name)#使用未绑定方法调用 People 类构造方法
#调用其它父类的构造方法,需手动给 self 传值
Animal.__init__(self,food)
per = Person("zhangsan","熟食")
per.say()
per.display()
類的特殊方法
編輯魔法方法(Magic Methods),有時也叫做特殊方法(Special Methods)、雙下方法(Dunder Methods),是一些以雙下劃線(__)開始和結束的方法。這些方法通常是由 Python 解釋器在特定情況下自動調用的,用於實現對象的特定行為和操作。魔法方法的設計目的是使得你可以自定義類的行為,以便讓它們能夠像內建類型一樣工作。例如,定義對象加法、比較、索引、迭代等操作。
- __init__ : 初始化函數,在生成對象時調用
- __del__ : 析構函數,釋放對象時使用
- __repr__ : 直接輸入instanceName再回車時輸出一個字符串。
- __str__: print(instanceName)時輸出一個字符串。
- __len__: 獲得長度
- __enter__ 與 __exit__ :專用於with語句
- __new__:Metaclass構造函數
函數 | 間接形式 | 直接形式 |
---|---|---|
__getattr__ | getattr(A, B) | A.B |
__setattr__ | setattr(A, B, C) | A.B = C |
__delattr__ | delattr(A, B) | del A.B |
操作符重載,各個操作數必須都是該類的實例。增強賦值操作符(如 +=)時,Python 會先嘗試調用 就地運算符方法,如 __iadd__。如果沒有定義該方法,Python 會退回到調用普通的雙元運算符方法,例如 __add__。這些方法返回一個新的對象,並將其重新賦值給左側的變量。 __iadd__中的字母 i 代表 "in-place",即 "就地" 的意思。
函數 | 運算符 |
---|---|
__add__ | A + B |
__sub__ | A - B |
__mul__ | A * B |
__truediv__ | A / B |
__floordiv__ | A // B |
__mod__ | A % B |
__pow__ | A ** B |
__and__ | A & B |
__or__ | A | B |
__xor__ | A ^ B |
__eq__ | A == B |
__ne__ | A != B |
__gt__ | A > B |
__lt__ | A < B |
__ge__ | A >= B |
__le__ | A <= B |
__lshift__ | A << B |
__rshift__ | A >> B |
__contains__ | A in B A not in B |
函數 | 運算符 |
---|---|
__pos__ | +A |
__neg__ | -A |
__inv__ | ~A |
__abs__ | abs(A) |
__len__ | len(A) |
函數 | 運算符 |
---|---|
__getitem__ | C[i] |
__setitem__ | C[i] = v |
__delitem__ | del C[i] |
__getslice__ | C[s:e] |
__setslice__ | C[s:e] = v |
__delslice__ | del C[s:e] |
函數 | 運算符 |
---|---|
__cmp__ | cmp(x, y) Python3棄用 |
__hash__ | hash(x) |
__nonzero__ | bool(x) |
__call__ | instanceName(params) |
__iter__ | iter(x) |
__reversed__ | reversed(x) 逆序迭代器 |
__divmod__ | divmod(x, y) |
__int__ | int(x) |
__long__ | long(x) |
__float__ | float(x) |
__complex__ | complex(x) |
__hex__ | hex(x) |
__oct__ | oct(x) |
__index__ | 在需要整數的場景中返回一個整數值,可用於bin()、oct()、hex()、位運算符如 <<(左移)和 >>(右移)、作為range() 函數的參數、作為list、元組、字符串等的下標 |
__copy__ | copy.copy(x) |
__deepcopy__ | copy.deepcopy(x) |
__sizeof__ | sys.getsizeof(x) (2.6+) |
__trunc__ | math.trunc(x) (2.6+) |
__format__ | format(x, ...) (2.6+) |
綜合示例
編輯import math
class MyComplex:
"""A complex number""" # Class documentation
classvar = 0.0 # A class attribute, not an instance one
def phase(self): # A method
return math.atan2(self.imaginary, self.real)
def __init__(self): # A constructor
"""A constructor"""
self.real = 0.0 # An instance attribute
self.imaginary = 0.0
c1 = MyComplex()
c1.real = 3.14 # No access protection
c1.imaginary = 2.71
phase = c1.phase() # Method call
c1.undeclared = 9.99 # Add an instance attribute
del c1.undeclared # Delete an instance attribute
print(vars(c1)) # Attributes as a dictionary. vars() 的用法是返回对象的 __dict__ 属性。
vars(c1)["undeclared2"] = 7.77 # Write access to an attribute
print(c1.undeclared2) # 7.77, indeed
MyComplex.classvar = 1 # Class attribute access
print(c1.classvar == 1) # True; class attribute access, not an instance one
print("classvar" in vars(c1)) # False
c1.classvar = -1 # An instance attribute overshadowing the class one
MyComplex.classvar = 2 # Class attribute access
print(c1.classvar == -1) # True; instance attribute acccess
print("classvar" in vars(c1)) # True
class MyComplex2(MyComplex): # Class derivation or inheritance
def __init__(self, re = 0, im = 0):
self.real = re # A constructor with multiple arguments with defaults
self.imaginary = im
def phase(self):
print "Derived phase"
return MyComplex.phase(self) # Call to a base class; "super"
c3 = MyComplex2()
c4 = MyComplex2(1, 1)
c4.phase() # Call to the method in the derived class
class Record: pass # Class as a record/struct with arbitrary attributes
record = Record()
record.name = "Joe"
record.surname = "Hoe"
新風格類
編輯從python 2.2開始支持新風格類(new-style class)。Python 3已經都是新風格類了,即使是按照老風格寫的代碼。新風格類(new-style class)是指繼承自 object 基類的類。在 Python 3 中,所有類都隱式繼承自 object,即使你沒有顯式地寫 class MyClass(object):,Python 3 會自動將其作為新風格類處理。
- 新風格類支持 __class__ 屬性,這是指向類對象的引用。你可以通過 object.__class__ 來訪問當前對象所屬的類。
- 新風格類中,super() 函數用來調用父類的方法,它提供了一種調用父類方法的統一方式。特別是在多繼承中,super() 可以確保方法解析順序(MRO,Method Resolution Order)按正確順序調用。
- 新風格類支持描述符協議(Descriptors),允許自定義如何訪問對象的屬性。描述符協議主要涉及三個方法:__get__、__set__ 和 __delete__,這些方法在屬性訪問、設置和刪除時會被自動調用。
- 新風格類支持元類(Metaclasses)的概念,元類用於創建類,通過元類,你可以控制類的創建過程、修改類的行為等。
- 新風格類引入了方法解析順序(MRO),它是多繼承時決定方法調用順序的一種規則。你可以通過 class.mro() 或 super().mro() 查看類的 MRO。
Classic Class:
>>> class ClassicFoo:
... def __init__(self):
... pass
New Style Class:
>>> class NewStyleFoo(object):
... def __init__(self):
... pass
屬性
編輯屬性(Property)是有getter與setter方法。
>>> class SpamWithProperties(object):
... def __init__(self):
... self.__egg = "MyEgg"
... def get_egg(self):
... return self.__egg
... def set_egg(self, egg):
... self.__egg = egg
... egg = property(get_egg, set_egg)
>>> sp = SpamWithProperties()
>>> sp.egg
'MyEgg'
>>> sp.egg = "Eggs With Spam"
>>> sp.egg
'Eggs With Spam'
>>>
從Python 2.6開始有@property decorator:
>>> class SpamWithProperties(object):
... def __init__(self):
... self.__egg = "MyEgg"
... @property
... def egg(self):
... return self.__egg
... @egg.setter
... def egg(self, egg):
... self.__egg = egg
靜態方法
編輯>>> class StaticSpam(object):
... def StaticNoSpam():
... print "You can't have have the spam, spam, eggs and spam without any spam... that's disgusting"
... NoSpam = staticmethod(StaticNoSpam)
>>> StaticSpam.NoSpam()
You can't have have the spam, spam, eggs and spam without any spam... that's disgusting
或使用函數decorator @staticmethod
>>> class StaticSpam(object):
... @staticmethod
... def StaticNoSpam():
... print "You can't have have the spam, spam, eggs and spam without any spam... that's disgusting"
菱形繼承的方法搜索順序
編輯Python 2.2開始,多繼承中父類的搜索順序是廣度優先MRO(Method Resolution Order),稱為C3算法。這樣的新式類有__mro__屬性,可以打印出父類搜索順序。
最佳實踐
編輯建議像C++那樣使用類。使用<class>.<member>語法,代替__dict__。
封裝
編輯私有(屬性或方法)名字,是在名字前綴2個或更多下劃線並且沒有後綴2個或多個下劃線。
所有的私有,都不能在類的外部使用。父類的私有屬性可以被子類調用嗎? 不可以。
Python 其實沒有真正的隱藏機制,雙下畫線只是 Python 的一個小技巧,Python 會「偷偷」地改變以雙下畫線開頭的方法名,會在這些方法名前添加單下畫線和類名。因此上面的 __method1() 方法其實可以按_className__method1方式調用(通常並不推薦這麼幹)。
Doc Strings
編輯鼓勵使用
運行時增加方法
編輯增加給類
編輯class Spam:
def __init__(self):
self.myeggs = 5
def cook(self):
print "cooking %s eggs" % self.myeggs
Spam.cook = cook #add the function to the class Spam
eggs = Spam() #NOW create a new instance of Spam
eggs.cook() #and we are ready to cook!
This will output
cooking 5 eggs
增加給實例
編輯class Spam:
def __init__(self):
self.myeggs = 5
eggs = Spam()
def cook(self):
print "cooking %s eggs" % self.myeggs
import types
f = types.MethodType(cook, eggs, Spam)
eggs.cook = f
eggs.cook()
輸出為:
cooking 5 eggs
使用一個函數
編輯寫一個函數來實現給類實例增加方法:
def attach_method(fxn, instance, myclass):
f = types.MethodType(fxn, instance, myclass)
setattr(instance, fxn.__name__, f)
attach_method(cook, eggs, Spam)
在函數add_method中不能寫instance.fxn = f因為它增加了一次函數調用fxn到這個實例