首頁 後端開發 Python教學 Python 的類別、繼承與多態詳解

Python 的類別、繼承與多態詳解

May 02, 2018 pm 03:36 PM
python 詳解

本文透過實例給大家詳細解釋了Python 的類別、繼承和多型的定義和用法,非常實用,有需要的小夥伴可以參考下

##類別的定義

假如要定義一個類別Point,表示二維的座標點:

# point.py
class Point:
  def __init__(self, x=0, y=0):
    self.x, self.y = x, y
登入後複製

最最基本的就是__init__ 方法,相當於C / Java 的建構子。雙底線 __ 的方法都是特殊方法,除了 __init__ 還有很多,後面會有介紹。

參數 self 相當於 C 的 this,表示目前實例,所有方法都有這個參數,但呼叫時並不需要指定。

>>> from point import *
>>> p = Point(10, 10) # __init__ 被调用
>>> type(p)
<class &#39;point.Point&#39;>
>>> p.x, p.y
(10, 10)
登入後複製

幾乎所有的特殊方法(包括 __init__)都是隱含呼叫的(不直接呼叫)。

對一切皆物件的Python 來說,類別自己當然也是物件:

>>> type(Point)
<class &#39;type&#39;>
>>> dir(Point)
[&#39;__class__&#39;, &#39;__delattr__&#39;, &#39;__dict__&#39;, ..., &#39;__init__&#39;, ...]
>>> Point.__class__
<class &#39;type&#39;>
登入後複製

Point 是type 的一個實例,這和p是Point 的一個實例是一回事。

現新增方法設定:

class Point:
  ...
  def set(self, x, y):
    self.x, self.y = x, y
登入後複製

#

>>> p = Point(10, 10)
>>> p.set(0, 0)
>>> p.x, p.y
(0, 0)
登入後複製

p.set(... ) 其實只是一個語法糖,你也可以寫成Point.set(p, ...),這樣就能明顯看出p 就是self 參數了:

>>> Point.set(p, 0, 0)
>>> p.x, p.y
(0, 0)
登入後複製

值得注意的是,self 並不是關鍵字,甚至可以用其它名字替代,例如this:

class Point:
  ...
  def set(this, x, y):
    this.x, this.y = x, y
登入後複製

與C 不同的是,「成員變數」必須要加self. 前綴,否則就變成類別的屬性(相當於C 靜態成員),而不是物件的屬性了。

存取控制

Python 沒有 public / protected / private 這樣的存取控制,如果你非要表示“私有”,習慣是加雙底線前綴。

class Point:
  def __init__(self, x=0, y=0):
    self.__x, self.__y = x, y

  def set(self, x, y):
    self.__x, self.__y = x, y

  def __f(self):
    pass
登入後複製

__x、__y 和__f 就相當於私有了:

>>> p = Point(10, 10)
>>> p.__x
...
AttributeError: &#39;Point&#39; object has no attribute &#39;__x&#39;
>>> p.__f()
...
AttributeError: &#39;Point&#39; object has no attribute &#39;__f&#39;
登入後複製

_repr_

嘗試列印Point 實例:

#

>>> p = Point(10, 10)
>>> p
<point.Point object at 0x000000000272AA20>
登入後複製

通常,這不是我們想要的輸出,我們想要的是:

>>> p
Point(10, 10)
登入後複製

新增特殊方法__repr__ 即可實作:

class Point:
  def __repr__(self):
    return &#39;Point({}, {})&#39;.format(self.__x, self.__y)
登入後複製

不難看出,互動模式在列印p 時其實是呼叫了repr(p):

>>> repr(p)

'Point(10, 10)'

_str_

如果沒有提供__str__,str() 預設使用repr() 的結果。

 這兩者都是物件的字串形式的表示,但還是有點差異的。簡單來說,repr() 的結果面向的是解釋器,通常都是合法的 Python 程式碼,例如 Point(10, 10);而 str() 的結果面向用戶,更簡潔,例如 (10, 10)。

依照這個原則,我們提供Point __str__ 的定義如下:

class Point:
  def __str__(self):
    return &#39;({}, {})&#39;.format(self.__x, self.__y)
登入後複製

##_add_


##兩個座標點相加是個很合理的需求。

>>> p1 = Point(10, 10)
>>> p2 = Point(10, 10)
>>> p3 = p1 + p2
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: &#39;Point&#39; and &#39;Point&#39;
登入後複製

新增特殊方法__add__ 即可做到:

##

class Point:
  def __add__(self, other):
    return Point(self.__x + other.__x, self.__y + other.__y)
登入後複製

>>> p3 = p1 + p2
>>> p3
Point(20, 20)
登入後複製

這就像C 裡的運算元重載一樣。

Python 的內建類型,例如字串、列表,都「重載」了 操作符。

特殊方法還有很多,這裡就不逐一介紹了。

繼承

舉出一個教科書中最常見的例子。 Circle 和 Rectangle 繼承自 Shape,不同的圖形,面積(area)計算方式不同。

# shape.py

class Shape:
  def area(self):
    return 0.0
    
class Circle(Shape):
  def __init__(self, r=0.0):
    self.r = r

  def area(self):
    return math.pi * self.r * self.r

class Rectangle(Shape):
  def __init__(self, a, b):
    self.a, self.b = a, b

  def area(self):
    return self.a * self.b
登入後複製

用法比較直接:

>>> from shape import *
>>> circle = Circle(3.0)
>>> circle.area()
28.274333882308138
>>> rectangle = Rectangle(2.0, 3.0)
>>> rectangle.area()
6.0
登入後複製

如果Circle 沒有定義自己的area:

class Circle(Shape):
  pass
登入後複製

那麼它將繼承父類別Shape 的area:

>>> Shape.area is Circle.area
True
登入後複製

一旦Circle定義了自己的area,從Shape 繼承而來的那個area 就被重寫(overwrite)了:

>>> from shape import *
>>> Shape.area is Circle.area
False
登入後複製

透過類別的字典更能明顯地看清楚這一點:

>>> Shape.__dict__[&#39;area&#39;]
<function Shape.area at 0x0000000001FDB9D8>
>>> Circle.__dict__[&#39;area&#39;]
<function Circle.area at 0x0000000001FDBB70>
登入後複製

所以,子類別重寫父類別的方法,其實只是把相同的屬性名稱綁定到了不同的函數物件。可見 Python 是沒有覆寫(override)的概念的。

同理,即使 Shape 沒有定義 area 也是可以的,Shape 作為“介面”,並不能得到語法的保證。 甚至可以動態的新增方法:

class Circle(Shape):
  ...
  # def area(self):
    # return math.pi * self.r * self.r

# 为 Circle 添加 area 方法。
Circle.area = lambda self: math.pi * self.r * self.r
登入後複製
#########動態語言通常都這麼靈活,Python 也不例外。 ######Python 官方教學「9. Classes」第一句就是:######Compared with other programming languages, Python's class mechanism adds classes with a minimum of new syntax and semantics.##### #Python 以最少的新的語法和語意實現了類別機制,這確實讓人驚嘆,但是也讓C / Java 程式設計師感到相當不適。 #########多態#########如前所述,Python 沒有覆寫(override)的概念。嚴格來講,Python 並不支援「多型」。 ###

为了解决继承结构中接口和实现的问题,或者说为了更好的用 Python 面向接口编程(设计模式所提倡的),我们需要人为的设一些规范。

请考虑 Shape.area() 除了简单的返回 0.0,有没有更好的实现?

以内建模块 asyncio 为例,AbstractEventLoop 原则上是一个接口,类似于 Java 中的接口或 C++ 中的纯虚类,但是 Python 并没有语法去保证这一点,为了尽量体现 AbstractEventLoop 是一个接口,首先在名字上标志它是抽象的(Abstract),然后让每个方法都抛出异常 NotImplementedError。

class AbstractEventLoop:
  def run_forever(self):
    raise NotImplementedError
  ...
登入後複製

纵然如此,你是无法禁止用户实例化 AbstractEventLoop 的:

loop = asyncio.AbstractEventLoop()
try:
  loop.run_forever()
except NotImplementedError:
  pass
登入後複製

C++ 可以通过纯虚函数或设构造函数为 protected 来避免接口被实例化,Java 就更不用说了,接口就是接口,有完整的语法支持。

你也无法强制子类必须实现“接口”中定义的每一个方法,C++ 的纯虚函数可以强制这一点(Java 更不必说)。

就算子类「自以为」实现了“接口”中的方法,也不能保证方法的名字没有写错,C++ 的 override 关键字可以保证这一点(Java 更不必说)。

静态类型的缺失,让 Python 很难实现 C++ / Java 那样严格的多态检查机制。所以面向接口的编程,对 Python 来说,更多的要依靠程序员的素养。

回到 Shape 的例子,仿照 asyncio,我们把“接口”改成这样:

class AbstractShape:
  def area(self):
    raise NotImplementedError
登入後複製

这样,它才更像一个接口。

super

有时候,需要在子类中调用父类的方法。

比如图形都有颜色这个属性,所以不妨加一个参数 color 到 __init__:

class AbstractShape:
  def __init__(self, color):
    self.color = color
登入後複製

那么子类的 __init__() 势必也要跟着改动:

class Circle(AbstractShape):
  def __init__(self, color, r=0.0):
    super().__init__(color)
    self.r = r
登入後複製

通过 super 把 color 传给父类的 __init__()。其实不用 super 也行:

class Circle(AbstractShape):
  def __init__(self, color, r=0.0):
    AbstractShape.__init__(self, color)
    self.r = r
登入後複製

但是 super 是推荐的做法,因为它避免了硬编码,也能处理多继承的情况。

相关推荐:

Python的环境配置解析


以上是Python 的類別、繼承與多態詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

sublime怎麼運行代碼python sublime怎麼運行代碼python Apr 16, 2025 am 08:48 AM

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

vs code 可以在 Windows 8 中運行嗎 vs code 可以在 Windows 8 中運行嗎 Apr 15, 2025 pm 07:24 PM

VS Code可以在Windows 8上運行,但體驗可能不佳。首先確保系統已更新到最新補丁,然後下載與系統架構匹配的VS Code安裝包,按照提示安裝。安裝後,注意某些擴展程序可能與Windows 8不兼容,需要尋找替代擴展或在虛擬機中使用更新的Windows系統。安裝必要的擴展,檢查是否正常工作。儘管VS Code在Windows 8上可行,但建議升級到更新的Windows系統以獲得更好的開發體驗和安全保障。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

visual studio code 可以用於 python 嗎 visual studio code 可以用於 python 嗎 Apr 15, 2025 pm 08:18 PM

VS Code 可用於編寫 Python,並提供許多功能,使其成為開發 Python 應用程序的理想工具。它允許用戶:安裝 Python 擴展,以獲得代碼補全、語法高亮和調試等功能。使用調試器逐步跟踪代碼,查找和修復錯誤。集成 Git,進行版本控制。使用代碼格式化工具,保持代碼一致性。使用 Linting 工具,提前發現潛在問題。

See all articles