日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Python如何設(shè)計(jì)面向?qū)ο蟮念悾ㄏ拢?/div>

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

創(chuàng)新互聯(lián)專注于遂寧企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè),成都做商城網(wǎng)站。遂寧網(wǎng)站建設(shè)公司,為遂寧等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

第1版:兼容Vector2d類

代碼如下:

 
 
 
  1. from array import array
  2. import reprlib
  3. import math
  4. class Vector:
  5.     typecode = 'd'
  6.     def __init__(self, components):
  7.         self._components = array(self.typecode, components)  # 多維向量存數(shù)組中
  8.     def __iter__(self):
  9.         return iter(self._components)  # 構(gòu)建迭代器
  10.     def __repr__(self):
  11.         components = reprlib.repr(self._components)  # 有限長(zhǎng)度表示形式
  12.         components = components[components.find('['):-1]
  13.         return 'Vector({})'.format(components)
  14.     def __str__(self):
  15.         return str(tuple(self))
  16.     def __bytes__(self):
  17.         return (bytes([ord(self.typecode)]) +
  18.                 bytes(self._components))
  19.     def __eq__(self, other):
  20.         return tuple(self) == tuple(other)
  21.     def __abs__(self):
  22.         return math.sqrt(sum(x * x for x in self))
  23.     def __bool__(self):
  24.         return bool(abs(self))
  25.     @classmethod
  26.     def frombytes(cls, octets):
  27.         typecode = chr(octets[0])
  28.         memv = memoryview(octets[1:]).cast(typecode)
  29.         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, ...])

超過6個(gè)的元素用...來表示。

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

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

值得說明的是,__getattr__的機(jī)制是:對(duì)my_obj.x表達(dá)式,Python會(huì)檢查my_obj實(shí)例有沒有名為x的屬性,如果有就直接返回,不調(diào)用__getattr__方法;如果沒有,到my_obj.__class__中查找,如果還沒有,才調(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á)式來說,直接就返回了這個(gè)值,不會(huì)走我們自定義的__getattr__方法,也就沒辦法拿到v[0]的值。

第4版:散列

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

代碼如下:

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

其中__eq__方法做了下修改,用到了歸約函數(shù)all(),比tuple(self) == tuple(other)的寫法,能減少處理時(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. def angles(self):
  9.     return (self.angle(n) for n in range(1, len(self)))
  10. def __format__(self, fmt_spec=''):
  11.     if fmt_spec.endswith('h'):  # hyperspherical coordinates
  12.         fmt_spec = fmt_spec[:-1]
  13.         coords = itertools.chain([abs(self)],
  14.                                  self.angles())
  15.         outer_fmt = '<{}>'
  16.     else:
  17.         coords = self
  18.         outer_fmt = '({})'
  19.     components = (format(c, fmt_spec) for c in coords)
  20.     return outer_fmt.format(', '.join(components))

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

小結(jié)

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


網(wǎng)站名稱:Python如何設(shè)計(jì)面向?qū)ο蟮念悾ㄏ拢?
分享URL:http://www.5511xx.com/article/ccceodo.html