自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python如何設(shè)計(jì)面向?qū)ο蟮念悾ㄉ希?/h1>

開發(fā) 后端
Python是一門高級(jí)語言,支持面向?qū)ο笤O(shè)計(jì),如何設(shè)計(jì)一個(gè)符合Python風(fēng)格的面向?qū)ο蟮念?,是一個(gè)比較復(fù)雜的問題,本文提供一個(gè)參考,表達(dá)一種思路,探究一層原理。

 [[408922]]

本文轉(zhuǎn)載自微信公眾號(hào)「dongfanger」,作者dongfanger。轉(zhuǎn)載本文請(qǐng)聯(lián)系dongfanger公眾號(hào)。

Python是一門高級(jí)語言,支持面向?qū)ο笤O(shè)計(jì),如何設(shè)計(jì)一個(gè)符合Python風(fēng)格的面向?qū)ο蟮念?,是一個(gè)比較復(fù)雜的問題,本文提供一個(gè)參考,表達(dá)一種思路,探究一層原理。

目標(biāo)

期望實(shí)現(xiàn)的類具有以下基本行為:

  • __repr__ 為repr()提供支持,返回便于開發(fā)者理解的對(duì)象字符串表示形式。
  • __str__ 為str()提供支持,返回便于用戶理解的對(duì)象字符串表示形式。
  • __bytes__ 為bytes()提供支持,返回對(duì)象的二進(jìn)制表示形式。
  • __format__ 為format()和str.format()提供支持,使用特殊的格式代碼顯示對(duì)象的字符串表示形式。

Vector2d是一個(gè)向量類,期望它能支持以下操作:

  1. >>> v1 = Vector2d(3, 4) 
  2. >>> print(v1.x, v1.y)  # 通過屬性直接訪問 
  3. 3.0 4.0 
  4. >>> x, y = v1  # 支持拆包 
  5. >>> x, y 
  6. (3.0, 4.0) 
  7. >>> v1  # 支持repr 
  8. Vector2d(3.0, 4.0) 
  9. >>> v1_clone = eval(repr(v1))  # 驗(yàn)證repr描述準(zhǔn)確 
  10. >>> v1 == v1_clone  # 支持==運(yùn)算符 
  11. True 
  12. >>> print(v1)  # 支持str 
  13. (3.0, 4.0) 
  14. >>> octets = bytes(v1)  # 支持bytes 
  15. >>> octets 
  16. b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@' 
  17. >>> abs(v1)  # 實(shí)現(xiàn)__abs__ 
  18. 5.0 
  19. >>> bool(v1), bool(Vector2d(0, 0))  # 實(shí)現(xiàn)__bool__ 
  20. (TrueFalse

基本實(shí)現(xiàn)

代碼與解析如下:

  1. from array import array 
  2. import math 
  3.  
  4.  
  5. class Vector2d: 
  6.     # Vector2d實(shí)例和二進(jìn)制之間轉(zhuǎn)換時(shí)使用 
  7.     typecode = 'd'   
  8.  
  9.     def __init__(self, x, y): 
  10.         # 轉(zhuǎn)換為浮點(diǎn)數(shù) 
  11.         self.x = float(x)     
  12.         self.y = float(y) 
  13.  
  14.     def __iter__(self): 
  15.         # 生成器表達(dá)式,把Vector2d實(shí)例變成可迭代對(duì)象,這樣才能拆包 
  16.         return (i for i in (self.x, self.y))   
  17.  
  18.     def __repr__(self): 
  19.         class_name = type(self).__name__ 
  20.         # {!r}是個(gè)萬能的格式符 
  21.         # *self是拆包,*表示所有元素 
  22.         return '{}({!r}, {!r})'.format(class_name, *self) 
  23.  
  24.     def __str__(self): 
  25.         # Vector2d實(shí)例是可迭代對(duì)象,可以得到一個(gè)元組,并str 
  26.         return str(tuple(self)) 
  27.  
  28.     def __bytes__(self): 
  29.         # 轉(zhuǎn)換為二進(jìn)制 
  30.         return (bytes([ord(self.typecode)]) +   
  31.                 bytes(array(self.typecode, self)))   
  32.  
  33.     def __eq__(self, other): 
  34.         # 比較相等 
  35.         return tuple(self) == tuple(other)   
  36.  
  37.     def __abs__(self): 
  38.         # 向量的模是直角三角形的斜邊長(zhǎng) 
  39.         return math.hypot(self.x, self.y)  
  40.  
  41.     def __bool__(self): 
  42.         # 0.0是False,非零值是True 
  43.         return bool(abs(self))   
  44.      
  45.     @classmethod 
  46.     def frombytes(cls, octets):  # classmethod不傳self傳cls 
  47.         typecode = chr(octets[0]) 
  48.         memv = memoryview(octets[1:]).cast(typecode) 
  49.         return cls(*memv)  # 拆包后得到構(gòu)造方法所需的一對(duì)參數(shù) 

代碼最后用到了@classmethod裝飾器,它容易跟@staticmethod混淆。

@classmethod的用法是:定義操作類,而不是操作實(shí)例的方法。常用來定義備選構(gòu)造方法。

@staticmethod其實(shí)就是個(gè)普通函數(shù),只不過剛好放在了類的定義體里。實(shí)際定義在類中或模塊中都可以。

格式化顯示

代碼與解析如下:

  1. def angle(self): 
  2.     return math.atan2(self.y, self.x) 
  3.  
  4.  
  5. def __format__(self, fmt_spec=''): 
  6.     if fmt_spec.endswith('p'):  # 以'p'結(jié)尾,使用極坐標(biāo) 
  7.         fmt_spec = fmt_spec[:-1] 
  8.         coords = (abs(self), self.angle())  # 計(jì)算極坐標(biāo)(magnitude, angle) 
  9.         outer_fmt = '<{}, {}>'  # 尖括號(hào) 
  10.     else
  11.         coords = self  # 不以'p'結(jié)尾,構(gòu)建直角坐標(biāo)(x, y) 
  12.         outer_fmt = '({}, {})'  # 圓括號(hào) 
  13.     components = (format(c, fmt_spec) for c in coords)  # 使用內(nèi)置format函數(shù)格式化字符串 
  14.     return outer_fmt.format(*components)  # 拆包后代入外層格式 

它實(shí)現(xiàn)了以下效果:

直角坐標(biāo):

  1. >>> format(v1) 
  2. '(3.0, 4.0)' 
  3. >>> format(v1, '.2f'
  4. '(3.00, 4.00)' 
  5. >>> format(v1, '.3e'
  6. '(3.000e+00, 4.000e+00)' 

極坐標(biāo):

  1. >>> format(Vector2d(1, 1), 'p')  # doctest:+ELLIPSIS 
  2. '<1.414213..., 0.785398...>' 
  3. >>> format(Vector2d(1, 1), '.3ep'
  4. '<1.414e+00, 7.854e-01>' 
  5. >>> format(Vector2d(1, 1), '0.5fp'
  6. '<1.41421, 0.78540>' 

可散列的

實(shí)現(xiàn)__hash__特殊方法能讓Vector2d變成可散列的,不過在這之前需要先讓屬性不可變,代碼如下:

  1. def __init__(self, x, y): 
  2.     # 雙下劃線前綴,變成私有的 
  3.     self.__x = float(x) 
  4.     self.__y = float(y) 
  5.  
  6. @property  # 標(biāo)記為特性 
  7. def x(self): 
  8.     return self.__x 
  9.  
  10. @property 
  11. def y(self): 
  12.     return self.__y 

這樣x和y就只讀不可寫了。

屬性名字的雙下劃線前綴叫做名稱改寫(name mangling),相當(dāng)于_Vector2d__x和_Vector2d__y,能避免被子類覆蓋。

然后使用位運(yùn)算符異或混合x和y的散列值:

  1. def __hash__(self): 
  2.     return hash(self.x) ^ hash(self.y) 

節(jié)省內(nèi)存

Python默認(rèn)會(huì)把實(shí)例屬性存儲(chǔ)在__dict__字典里,字典的底層是散列表,數(shù)據(jù)量大了以后會(huì)消耗大量?jī)?nèi)存(以空間換時(shí)間)。通過__slots__類屬性,能把實(shí)例屬性存儲(chǔ)到元組里,大大節(jié)省內(nèi)存空間。

示例:

  1. class Vector2d: 
  2.     __slots__ = ('__x''__y'
  3.  
  4.     typecode = 'd' 

有幾點(diǎn)需要注意:

必須把所有屬性都定義到__slots__元組中。

子類也必須定義__slots__。

實(shí)例如果要支持弱引用,需要把__weakref也加入__slots__。

覆蓋類屬性

實(shí)例覆蓋

Python有個(gè)很獨(dú)特的特性:類屬性可用于為實(shí)例屬性提供默認(rèn)值。實(shí)例代碼中的typecode就能直接被self.typecode拿到。但是,如果為不存在的實(shí)例屬性賦值,會(huì)新建實(shí)例屬性,類屬性不會(huì)受到影響,self.typecode拿到的是實(shí)例屬性的typecode。

示例:

  1. >>> v1 = Vector2d(1, 2) 
  2. >>> v1.typecode = 'f' 
  3. >>> v1.typecode 
  4. 'f' 
  5. >>> Vector2d.typecode 
  6. 'd' 

子類覆蓋

類屬性是公開的,所以可以直接通過Vector2d.typecode = 'f'進(jìn)行修改。但是更符合Python風(fēng)格的做法是定義子類:

  1. class ShortVector2d(Vector2d): 
  2.     typecode = 'f' 

Django基于類的視圖大量使用了這個(gè)技術(shù)。

小結(jié)

本文先介紹了如何實(shí)現(xiàn)特殊方法來設(shè)計(jì)一個(gè)Python風(fēng)格的類,然后分別實(shí)現(xiàn)了格式化顯示與可散列對(duì)象,使用__slots__能為類節(jié)省內(nèi)存,最后討論了類屬性覆蓋技術(shù),子類覆蓋是Django基于類的視圖大量用到的技術(shù)。

參考資料:

《流暢的Python》第9章 符合Python風(fēng)格的對(duì)象

https://www.jianshu.com/p/7fc0a177fd1f

 

責(zé)任編輯:武曉燕 來源: dongfanger
相關(guān)推薦

2021-07-16 10:23:47

Python設(shè)計(jì)對(duì)象

2010-02-02 13:15:26

Python類

2009-01-16 08:52:26

面向?qū)ο?/a>OOP編程

2023-09-27 23:28:28

Python編程

2013-04-17 10:46:54

面向?qū)ο?/a>

2012-03-14 10:48:05

C#

2024-05-10 09:28:57

Python面向?qū)ο?/a>代碼

2012-06-07 10:11:01

面向?qū)ο?/a>設(shè)計(jì)原則Java

2023-11-02 07:55:31

Python對(duì)象編程

2012-12-25 10:51:39

IBMdW

2010-03-18 13:43:40

python面向?qū)ο?/a>

2016-03-11 09:46:26

面向?qū)ο?/a>設(shè)計(jì)無狀態(tài)類

2022-04-01 10:27:04

面向?qū)ο?/a>串口協(xié)議代碼

2024-12-12 08:05:14

元類Python控制類

2011-07-05 15:22:04

程序設(shè)計(jì)

2010-07-08 10:47:42

UML面向?qū)ο?/a>

2011-07-05 15:59:57

面向?qū)ο缶幊?/a>

2013-06-07 11:31:36

面向?qū)ο?/a>設(shè)計(jì)模式

2010-06-10 10:03:42

UML面向?qū)ο?/a>

2011-07-05 16:05:43

面向?qū)ο缶幊?/a>
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)