成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

開(kāi)發(fā) 后端
經(jīng)過(guò)上下兩篇文章的介紹,我們知道了Python風(fēng)格的類是什么樣子的,跟常規(guī)的面向?qū)ο笤O(shè)計(jì)不同的是,Python的類通過(guò)魔法方法實(shí)現(xiàn)了Python協(xié)議,使Python類在使用時(shí)能夠享受到語(yǔ)法糖,不用通過(guò)get和set的方式來(lái)編寫(xiě)代碼。

[[411673]]

本文將在上篇文章二維向量Vector2d類的基礎(chǔ)上,定義表示多維向量的Vector類。

第1版:兼容Vector2d類

代碼如下:

  1. from array import array 
  2. import reprlib 
  3. import math 
  4.  
  5.  
  6. class Vector: 
  7.     typecode = 'd' 
  8.  
  9.     def __init__(self, components): 
  10.         self._components = array(self.typecode, components)  # 多維向量存數(shù)組中 
  11.  
  12.     def __iter__(self): 
  13.         return iter(self._components)  # 構(gòu)建迭代器 
  14.  
  15.     def __repr__(self): 
  16.         components = reprlib.repr(self._components)  # 有限長(zhǎng)度表示形式 
  17.         components = components[components.find('['):-1] 
  18.         return 'Vector({})'.format(components) 
  19.  
  20.     def __str__(self): 
  21.         return str(tuple(self)) 
  22.  
  23.     def __bytes__(self): 
  24.         return (bytes([ord(self.typecode)]) + 
  25.                 bytes(self._components)) 
  26.  
  27.     def __eq__(self, other): 
  28.         return tuple(self) == tuple(other) 
  29.  
  30.     def __abs__(self): 
  31.         return math.sqrt(sum(x * x for x in self)) 
  32.  
  33.     def __bool__(self): 
  34.         return bool(abs(self)) 
  35.  
  36.     @classmethod 
  37.     def frombytes(cls, octets): 
  38.         typecode = chr(octets[0]) 
  39.         memv = memoryview(octets[1:]).cast(typecode) 
  40.         return cls(memv)  # 因?yàn)闃?gòu)造函數(shù)入?yún)⑹菙?shù)組,所以不用再使用*拆包了 

其中的reprlib.repr()函數(shù)用于生成大型結(jié)構(gòu)或遞歸結(jié)構(gòu)的安全表達(dá)形式,比如:

  1. >>> Vector([3.1, 4.2]) 
  2. Vector([3.1, 4.2]) 
  3. >>> Vector((3, 4, 5)) 
  4. Vector([3.0, 4.0, 5.0]) 
  5. >>> Vector(range(10)) 
  6. Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...]) 

超過(guò)6個(gè)的元素用...來(lái)表示。

第2版:支持切片

Python協(xié)議是非正式的接口,只在文檔中定義,在代碼中不定義。比如Python的序列協(xié)議只需要__len__和__getitem__兩個(gè)方法,Python的迭代協(xié)議只需要__getitem__一個(gè)方法,它們不是正式的接口,只是Python程序員默認(rèn)的約定。

切片是序列才有的操作,所以Vector類要實(shí)現(xiàn)序列協(xié)議,也就是__len__和__getitem__兩個(gè)方法,代碼如下:

  1. def __len__(self): 
  2.     return len(self._components) 
  3.  
  4. def __getitem__(self, index): 
  5.     cls = type(self)  # 獲取實(shí)例所屬的類 
  6.     if isinstance(index, slice):  # 如果index是slice切片對(duì)象 
  7.         return cls(self._components[index])  # 調(diào)用構(gòu)造方法,返回新的Vector實(shí)例 
  8.     elif isinstance(index, numbers.Integral):  # 如果index是整型 
  9.         return self._components[index]  # 直接返回元素 
  10.     else
  11.         msg = '{cls.__name__} indices must be integers' 
  12.         raise TypeError(msg.format(cls=cls)) 

測(cè)試一下:

  1. >>> v7 = Vector(range(7)) 
  2. >>> v7[-1]  # <1> 
  3. 6.0 
  4. >>> v7[1:4]  # <2> 
  5. Vector([1.0, 2.0, 3.0]) 
  6. >>> v7[-1:]  # <3> 
  7. Vector([6.0]) 
  8. >>> v7[1,2]  # <4> 
  9. Traceback (most recent call last): 
  10.   ... 
  11. TypeError: Vector indices must be integers 

第3版:動(dòng)態(tài)存取屬性

通過(guò)實(shí)現(xiàn)__getattr__和__setattr__,我們可以對(duì)Vector類動(dòng)態(tài)存取屬性。這樣就能支持v.my_property = 1.1這樣的賦值。

如果使用__setitem__方法,那么只能支持v[0] = 1.1。

代碼如下:

  1. shortcut_names = 'xyzt'  # 4個(gè)分量屬性名 
  2.  
  3. def __getattr__(self, name): 
  4.     cls = type(self)  # 獲取實(shí)例所屬的類 
  5.     if len(name) == 1:  # 只有一個(gè)字母 
  6.         pos = cls.shortcut_names.find(name
  7.         if 0 <= pos < len(self._components):  # 落在范圍內(nèi) 
  8.             return self._components[pos] 
  9.     msg = '{.__name__!r} object has no attribute {!r}'  # <5> 
  10.     raise AttributeError(msg.format(cls, name)) 
  11.  
  12.  
  13. def __setattr__(self, name, value): 
  14.     cls = type(self) 
  15.     if len(name) == 1:   
  16.         if name in cls.shortcut_names:  # name是xyzt其中一個(gè)不能賦值 
  17.             error = 'readonly attribute {attr_name!r}' 
  18.         elif name.islower():  # 小寫(xiě)字母不能賦值,防止與xyzt混淆 
  19.             error = "can't set attributes 'a' to 'z' in {cls_name!r}" 
  20.         else
  21.             error = '' 
  22.         if error: 
  23.             msg = error.format(cls_name=cls.__name__, attr_name=name
  24.             raise AttributeError(msg) 
  25.     super().__setattr__(name, value)  # 其他name可以賦值 

值得說(shuō)明的是,__getattr__的機(jī)制是:對(duì)my_obj.x表達(dá)式,Python會(huì)檢查my_obj實(shí)例有沒(méi)有名為x的屬性,如果有就直接返回,不調(diào)用__getattr__方法;如果沒(méi)有,到my_obj.__class__中查找,如果還沒(méi)有,才調(diào)用__getattr__方法。

正因如此,name是xyzt其中一個(gè)時(shí)才不能賦值,否則會(huì)出現(xiàn)下面的奇怪現(xiàn)象:

  1. >>> v = Vector([range(5)]) 
  2. >>> v.x = 10 
  3. >>> v.x 
  4. 10 
  5. >>> v 
  6. Vector([0.0, 1.0, 2.0, 3.0, 4.0]) 

對(duì)v.x進(jìn)行了賦值,但實(shí)際未生效,因?yàn)橘x值后Vector新增了一個(gè)x屬性,值為10,對(duì)v.x表達(dá)式來(lái)說(shuō),直接就返回了這個(gè)值,不會(huì)走我們自定義的__getattr__方法,也就沒(méi)辦法拿到v[0]的值。

第4版:散列

通過(guò)實(shí)現(xiàn)__hash__方法,加上現(xiàn)有的__eq__方法,Vector實(shí)例就變成了可散列的對(duì)象。

代碼如下:

  1. import functools 
  2. import operator 
  3.  
  4.  
  5. def __eq__(self, other): 
  6.     return (len(self) == len(other) and 
  7.             all(a == b for a, b in zip(self, other))) 
  8.  
  9. def __hash__(self): 
  10.     hashes = (hash(x) for x in self)  # 創(chuàng)建一個(gè)生成器表達(dá)式 
  11.     return functools.reduce(operator.xor, hashes, 0)  # 計(jì)算聚合的散列值 

其中__eq__方法做了下修改,用到了歸約函數(shù)all(),比tuple(self) == tuple(other)的寫(xiě)法,能減少處理時(shí)間和內(nèi)存。

zip()函數(shù)取名自zipper拉鏈,把兩個(gè)序列咬合在一起。比如:

  1. >>> list(zip(range(3), 'ABC')) 
  2. [(0, 'A'), (1, 'B'), (2, 'C')] 

第5版:格式化

Vector的格式化跟Vector2d大同小異,都是定義__format__方法,只是計(jì)算方式從極坐標(biāo)換成了球面坐標(biāo):

  1. def angle(self, n): 
  2.     r = math.sqrt(sum(x * x for x in self[n:])) 
  3.     a = math.atan2(r, self[n-1]) 
  4.     if (n == len(self) - 1) and (self[-1] < 0): 
  5.         return math.pi * 2 - a 
  6.     else
  7.         return a 
  8.  
  9. def angles(self): 
  10.     return (self.angle(n) for n in range(1, len(self))) 
  11.  
  12. def __format__(self, fmt_spec=''): 
  13.     if fmt_spec.endswith('h'):  # hyperspherical coordinates 
  14.         fmt_spec = fmt_spec[:-1] 
  15.         coords = itertools.chain([abs(self)], 
  16.                                  self.angles()) 
  17.         outer_fmt = '<{}>' 
  18.     else
  19.         coords = self 
  20.         outer_fmt = '({})' 
  21.     components = (format(c, fmt_spec) for c in coords) 
  22.     return outer_fmt.format(', '.join(components)) 

極坐標(biāo)和球面坐標(biāo)是啥?我也不知道,略過(guò)就好。

小結(jié)

經(jīng)過(guò)上下兩篇文章的介紹,我們知道了Python風(fēng)格的類是什么樣子的,跟常規(guī)的面向?qū)ο笤O(shè)計(jì)不同的是,Python的類通過(guò)魔法方法實(shí)現(xiàn)了Python協(xié)議,使Python類在使用時(shí)能夠享受到語(yǔ)法糖,不用通過(guò)get和set的方式來(lái)編寫(xiě)代碼。

 

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

2021-07-02 14:14:14

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

2010-02-02 13:15:26

Python類

2009-01-16 08:52:26

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

2013-04-17 10:46:54

面向?qū)ο?/a>

2023-09-27 23:28:28

Python編程

2012-03-14 10:48:05

C#

2012-06-07 10:11:01

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

2024-05-10 09:28:57

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

2012-12-25 10:51:39

IBMdW

2023-11-02 07:55:31

Python對(duì)象編程

2010-03-18 13:43:40

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

2016-03-11 09:46:26

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

2022-04-01 10:27:04

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

2010-07-08 10:47:42

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

2011-07-05 15:22:04

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

2011-07-05 15:59:57

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

2024-12-12 08:05:14

元類Python控制類

2013-06-07 11:31:36

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

2011-07-05 16:05:43

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

2010-06-10 10:03:42

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

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

主站蜘蛛池模板: 久久久久免费精品国产 | 岛国av免费观看 | 91人人在线| 亚洲精品一区二区冲田杏梨 | 日本视频在线 | 久久综合九色综合欧美狠狠 | 午夜寂寞影院在线观看 | 激情三区 | 精品亚洲视频在线 | 国产欧美精品在线 | 免费xxxx大片国产在线 | 精品毛片| 久久最新 | 九色网址 | 免费看一区二区三区 | 黄色福利| 99re在线视频 | 久久久国产一区二区三区 | 亚洲视频一区在线观看 | 亚洲国产一 | 久久精品免费观看 | 欧美在线视频一区二区 | 欧美一二三 | 看av网| 日本免费一区二区三区四区 | 91精品久久久久久综合五月天 | av一区在线 | 亚洲日本成人 | 91色站| 免费观看一级特黄欧美大片 | 亚洲三区在线观看 | 一区影视 | 日韩影音 | 91精品久久久久久久久中文字幕 | 成人一区二区三区在线观看 | 久久99精品久久久久子伦 | 久久久www成人免费精品张筱雨 | 三级成人片 | 亚洲国产精品日本 | 国产精品久久久久久妇女6080 | 激情麻豆视频 |