跳至內容

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修飾符進行修飾。類方法推薦使用類名直接調用
  • 靜態方法:使用@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()

類的特殊方法

[編輯]
  • __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

操作符重載,各個操作數必須都是該類的實例。帶有賦值操作符如果沒有重載,默認調用相應的雙元運算符,結果值賦給自身這個名字。

二元運算符重載函數
函數 運算符
__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)
__hash__ hash(x)
__nonzero__ bool(x)
__call__ instanceName(params)
__iter__ iter(x)
__reversed__ reversed(x) (2.6+)
__divmod__ divmod(x, y)
__int__ int(x)
__long__ long(x)
__float__ float(x)
__complex__ complex(x)
__hex__ hex(x)
__oct__ oct(x)
__index__
__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(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已經都是新風格類了,即使是按照老風格寫的代碼。

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到這個實例