国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

2015/9/28Python基礎(19):類的定制和私有性

2019-11-14 16:59:04
字體:
來源:轉載
供稿:網友

用特殊方法定制類
前面我們講了方法的兩個重要方面:首先,方法必須在調用前被綁定(到它們相應類的某個實例中);其次,有兩個特殊方法可以分別作為構造器和解構器的功能,分別名為__init__()和__del__()。
事實上,__init__()和__del__()只是可自定義特殊方法集中的一部分。它們中有一些有預定義的默認行為,而其他一些則沒有,留到需要的時候去實現。這些特殊方法是Python中用來擴充類的強有力的方式,它們可實現:
模擬標準類型
重載草所附

特殊方法允許類通過重載標準操作符+,*,甚至包括分段下標及映射操作符操作[]來模擬標準類型。如同其他很多保留標識符,這些方法都是以雙下劃線開始以及結尾的。
以下是方法摘抄:
基本定制型 描述
C.__init__(self[, arg1, ...]) 構造器(帶一些可選的參數)
C.__new__(self[, arg1, ...]) 構造器(帶一些可選的參數);通常用在設置不變數據類型的子類。
C.__del__(self) 解構器
C.__str__(self) 可打印的字符輸出;內建 str()及 PRint 語句
C.__repr__(self) 運行時的字符串輸出;內建 repr() 和‘‘ 操作符
C.__unicode__(self) Unicode 字符串輸出;內建 unicode()
C.__call__(self, *args) 表示可調用的實例
C.__nonzero__(self) 為 object 定義 False 值;內建 bool() (從 2.2 版開始)
C.__len__(self) “長度”(可用于類);內建 len()

 

對象(值)比較
C.__cmp__(self, obj) 對象比較;內建 cmp()
C.__lt__(self, obj) and 小于/小于或等于;對應<及<=操作符
C.__gt__(self, obj) and 大于/大于或等于;對應>及>=操作符
C.__eq__(self, obj) and 等于/不等于;對應==,!=及<>操作符

屬性
C.__getattr__(self, attr) 獲取屬性;內建 getattr();僅當屬性沒有找到時調用
C.__setattr__(self, attr, val) 設置屬性
C.__delattr__(self, attr) 刪除屬性
C.__getattribute__(self, attr) 獲取屬性;內建 getattr();總是被調用
C.__get__(self, attr) (描述符)獲取屬性
C.__set__(self, attr, val) (描述符)設置屬性
C.__delete__(self, attr) (描述符)刪除屬性

 

定制類/模擬類型
數值類型:二進制操作符
C.__*add__(self, obj) 加;+操作符
C.__*sub__(self, obj) 減;-操作符
C.__*mul__(self, obj) 乘;*操作符
C.__*div__(self, obj) 除;/操作符
C.__*truediv__(self, obj) True 除;/操作符
C.__*floordiv__(self, obj) Floor 除;//操作符
C.__*mod__(self, obj) 取模/取余;%操作符
C.__*divmod__(self, obj) 除和取模;內建 divmod()
C.__*pow__(self, obj[, mod]) 乘冪;內建 pow();**操作符
C.__*lshift__(self, obj) 左移位;<<操作符
C.__*rshift__(self, obj) 右移;>>操作符
C.__*and__(self, obj) 按位與;&操作符
C.__*or__(self, obj) 按位或;|操作符
C.__*xor__(self, obj) 按位與或;^操作符

 

數值類型:一元操作符
C.__neg__(self) 一元負
C.__pos__(self) 一元正
C.__abs__(self) 絕對值;內建 abs()
C.__invert__(self) 按位求反;~操作符

數值類型:數值轉換
C.__complex__(self, com) 轉為 complex(復數);內建 complex()
C.__int__(self) 轉為 int;內建 int()
C.__long__(self) 轉為 long;內建 long()
C.__float__(self) 轉為 float;內建 float()

數值類型:基本表示法(String)
C.__oct__(self) 八進制表示;內建 oct()
C.__hex__(self) 十六進制表示;內建 hex()

數值類型:數值壓縮
C.__coerce__(self, num) 壓縮成同樣的數值類型;內建 coerce()
C.__index__(self) 在有必要時,壓縮可選的數值類型為整型(比如:用于切片索引等等)


序列類型
C.__len__(self) 序列中項的數目
C.__getitem__(self, ind) 得到單個序列元素
C.__setitem__(self, ind,val) 設置單個序列元素
C.__delitem__(self, ind) 刪除單個序列元素
C.__getslice__(self, ind1,ind2) 得到序列片斷
C.__setslice__(self, i1, i2,val) 設置序列片斷
C.__delslice__(self, ind1,ind2) 刪除序列片斷
C.__contains__(self, val) 測試序列成員;內建 in 關鍵字

C.__*add__(self,obj) 串連;+操作符
C.__*mul__(self,obj) 重復;*操作符
C.__iter__(self) 創建迭代類;內建 iter()


映射類型
C.__len__(self) mapping中的項的數目
C.__hash__(self) 散列(hash)函數值
C.__getitem__(self,key) 得到給定鍵(key)的值
C.__setitem__(self,key,val) 設置給定鍵(key)的值
C.__delitem__(self,key) 刪除給定鍵(key)的值
C.__missing__(self,key) 給定鍵如果不存在字典中,則提供一個默認值

基本的定制和對象(值)比較特殊方法在大多數類中都可以被實現,且沒有同任何特定的類型模型綁定。延后設置,也就是所謂的福比較,在Python2.1中加入。屬性組幫助管理你的類的實例屬性。這同樣獨立于模型。還有一個,__getattribute__(),它僅用在新式類中,我們將在后面章節中對它進行描述。
特殊方法中數值類型部分可以用來模擬很多數值操作,包括那些標準操作符、類型轉換、基本表示法及亞索。還有用來模擬序列和映射類型的特殊方法。實現這些類型的特殊方法將會重載操作符,以使它們可以處理你的class類型實例。
表格中,在他們的名字中,用星號通配符標注的數值二進制操作符則表示這些方法有多個版本,在名字上有些許不同。星號可代表在字符串中沒有額外的字符,或者一個簡單的"r"指明是一個右結合操作。

簡單定制
我們之前從Python類型中派生出了派生類RoundFloat。這次我們想創建一個苗條的例子,這樣可以對類的定制的工作方法有一個更好的理解。這種類的前提與其他類是一樣的:我們只需要一個類來保存浮點型,四舍五入,保留兩位小數位。

>>> class RoundFloatManual(object):  def __init__ (self, val):    assert isinstance(val, float),/      "Value must be a float!"    self.value = round(val, 2)

 

這個類接受一個浮點值——它斷言了傳遞給構造器的參數必須為一個浮點型——并且將其保存為實例屬性值。接下來創建這個類的實例:

>>> rfm = RoundFloatManual(12)Traceback (most recent call last):File "<pyshell#6>", line 1, in <module>rfm = RoundFloatManual(12)File "<pyshell#5>", line 4, in __init__"Value must be a float!"AssertionError: Value must be a float!>>> rfm = RoundFloatManual(1.23)>>> rfm<__main__.RoundFloatManual object at 0x02918A50>>>> print rfm<__main__.RoundFloatManual object at 0x02918A50>

 

輸入非法時,就不執行程序返回斷言錯誤。如果輸入正確,沒有輸出,但是當我們把這個對象輸出時,卻得不到我們需要的信息,調用print語句也沒有幫助。
print(使用 str()) 和真正的字符串對象表示(使用repr())都沒能顯示更多有關我們對象的信息。一個好的辦法是,去實現__str__()和__repr__()兩者之一,或者兩者都實現。讓我們來添加一個__str__()方法,以覆蓋默認的行為。

class RoundFloatManual(object):def __init__ (self, val):assert isinstance(val, float),/"Value must be a float!"self.value = round(val,2)def __str__(self):return str(self.value)>>> rfm = RoundFloatManual(4.568)>>> print rfm4.57

 


我們還有一些問題,一個問題是在解釋器中轉儲對象是,仍然顯示的是默認對象符號,如果我們想修復它,只需要覆蓋__repr__()。因為字符串表示法也是Python對象,我們可以讓__repr__()和__str__()的輸出一致。
我們可以讓__repr__()作為__str__()的一個別名:
__repr__ = __str__
現在,可以看到,同時具備了str()和repr()的輸出了:

>>> class RoundFloatManual(object):  def __init__ (self, val):    assert isinstance(val, float),/      "Value must be a float!"    self.value = round(val,2)  def __str__(self):    return str(self.value)  __repr__ = __str__>>> rfm = RoundFloatManual(4.568)>>> rfm4.57>>> print rfm4.57

 

在前幾天提到的RoundFloat例子里,我們沒有擔心對象的顯示問題,原因是__str__()和__repr__()作為float類的一部分已經為我們定義好了。我們只需要繼承他們就行了。而這一次需要做另外的工作,這就是派生的好處。


數值定制
這里將有一個實際的例子,假設我們需要創建一個簡單的應用,用于操作時間,精確到小時和分,用于跟蹤運行時間等。
我們創建一個Time60類,把整型的小時和分鐘作為輸入傳給構造器:

class Time60(object):  'Time60 - track hours and minutes'  def __init__ (self, hr, min):    self.hr = hr    self.min = min

 


1.顯示
如果我們想有一個有意義的輸出,就應該覆蓋__str__,有必要還要覆蓋__repr__。我們按這樣輸出時間:

def __str__ (self):  return '%d:%d' % (self.hr, self.min)__repr__ = __str__

現在我們就可以用這個類來實例化一些對象了

>>> mon = Time60(10, 30)>>> tue = Time60(11, 15)>>> print mon, tue10:30 11:15

 

2.加法
Python的重載操作很簡單。重載加號只要重載__add__()方法,如果合適還可以用__radd__()及__iadd__()
實現加法我們只需要把分和小時加載一塊兒,處理一下進位操作:

def __add__ (self, other):  newhr = self.hr + other.hr  newmin = self.min + other.min  if newmin >= 60:    newmin -= 60    newhr += 1  if newhr >= 24: newhr -= 24  return self. __class__(newhr, /               newmin)

 

3.原位加法
有了增量賦值,我們還希望有原位操作,比如說__iadd__(),用來支持像 mon += tue 這樣的操作,重載一個__i*__()這樣的方法的唯一秘密是必須返回self:

def __iadd__ (self, other):  self.hr += other.hr  self.min += other.min  if self.min >= 60:    self.min -= 60    self.hr += 1  if self.hr >= 24: self.hr -= 24  return self

 


這樣這個類可以算是初步完成,但在這個類中,還有很多需要優化和改良的地方。這里我們暫時不繼續工作它。

迭代器
我們可以用__iter__()和next()方法,來創建一個迭代器。介紹兩個例子。
1.隨機序列迭代器
我們給我們的類傳入一個初始序列,然后通next()去迭代,這個例子展示了隨機且無窮迭代。

from random import choiceclass RandSeq(object):  def __init__ (self, seq):    self.data = seq  def __iter__ (self):    return self  def next(self):    return choice(self.data)

 

2.任意項的迭代器
這是代碼:

class AnyIter(object):  def __init__ (self, data, safe=False):    self.safe = safe    self.iter = iter(data)  def __iter__ (self):    return self  def next(self, howmany=1):    retval = []    for eachItem in range(howmany):      try:        retval.append(self.iter.next())      except StopIteration:        if self.safe:          break        else:          raise    return retval

 


我們給出一個迭代器和一個安全標識符來創建這個對象。如果這個標識符為真,我們將在遍歷完這個迭代器前,返回所獲取的任意條目,但如果這個標識符為假,則在用戶請求過多的條目時,將會引發一個異常。錯綜復雜的核心在于next(),特別是它如何退出的。
在next()的最后一部分中,我們創建用于返回的一個列表項,并且調用對象的next()方法來獲得每一項條目。如果我們遍歷完列表,得到一個StopIteration異常,這時則檢查安全標識符。如果不安全,則將異常拋還給調用者;否則,退出并返回已經保存過的所有項。

>>> class AnyIter(object):  def __init__ (self, data, safe=False):    self.safe = safe    self.iter = iter(data)  def __iter__ (self):    return self  def next(self, howmany=1):    retval = []    for eachItem in range(howmany):      try:        retval.append(self.iter.next())      except StopIteration:        if self.safe:          break        else:          raise    return retval>>> a = AnyIter(range(10))>>> i = iter(a)>>> for j in range(1,5):    print j, ':', i.next(j)1 : [0]2 : [1, 2]3 : [3, 4, 5]4 : [6, 7, 8, 9]

 

上面程序的運行沒有問題,因為迭代器正好符合項的個數。當情況出現偏差,會發生什么呢?讓我們首先試試“不安全”的模式,這就是緊隨其后創建我們的迭代器:

>>> i = iter(a)>>> i.next(14)Traceback (most recent call last):File "<pyshell#10>", line 1, in <module>i.next(14)File "<pyshell#3>", line 11, in nextretval.append(self.iter.next())StopIteration

 

因為超出了項的支持量,所以出現了StopIteration異常,并且這個異常還被重新引發回調用者。如果我們以“安全”模式重建迭代器,再次運行一次同一個例子的話,我們就可以在項失控前得到迭代器所得到的元素:

>>> a = AnyIter(range(10), True)>>> i = iter(a)>>> i.next(14)[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

多類型定制
我們創造一個新類,NumStr,由一個數字-字符對組成,相應地,記為n和s,數值類型使用整型。盡管這組順序對的合適的記號是(n, s)但是我們選用[n::s]來表示它。暫不管這個記號,我們只需要考慮數據元素模型就好了,是一個整體。這個新類有這樣的特征:
初始化:對數字和字符串進行初始化,如果其中一(或二)個沒有初始化,數字用0,字符串用空字符串作為默認參數。
加法:功能是數字相加,字符串按順序相連。
乘法:數字相乘,字符串累積相連。
False值:當數字為0且字符串為空是,這個實體有一個False值。
比較:一對NumStr對象比較應該有九種不同的組合(n1>n2 and s1<s2,n1 == n2 and s1>s2等等)。對數字和字符串,我們按標準的數值和字典順序進行比較,然后將比較的值相加返回結果。
代碼如下:

#!/usr/bin/env pythonclass NumStr(object):    def __init__(self, num=0, string=''):        self.__num = num        self.__string = string    def __str__(self):          # define for str()        return '[%d :: %r]' % /            (self.__num, self.__string)    __repr__ = __str__    def __add__(self, other):           # define for s+o        if isinstance(other, NumStr):            return self.__class__(self.__num + /                other.__num,                self.__string + other.__string)        else:            raise TypeError, /'Illegal argument type for built-in Operation'    def __mul__(self, num):           # define for s*o        if isinstance(num, int):            return self.__class__(self.__num * num,                self.__string * num)        else:            raise TypeError, /'Illegal argument type for built-in operation'    def __nonzero__(self):              # reveal tautology        return self.__num or len(self.__string)    def __norm_cval(self, cmpres):      # normalize cmp()        return cmp(cmpres, 0)    def __cmp__(self, other):           # define for cmp()        return self.__norm_cval(                cmp(self.__num, other.__num)) + /            self.__norm_cval(                cmp(self.__string, other.__string))

至此,定制類暫告一段落,雖然對名稱的命名還存在很多疑問,為什么有下劃線和雙下劃線一直讓人很困擾。接下來學習私有化。

私有化
默認情況下,屬性在Python中都是“public”,類所在模塊和導入了類所在模塊的其他代碼都可以訪問到。很多OO語言給數據加上一些可見性,只提供訪問函數來訪問其值。這就是熟知的實現隱藏,是對象封裝的一個關鍵部分。
大多數OO語言提供“訪問控制符”來限定成員函數的訪問。
而Python只為類屬性的私有性提供了初步的訪問形式。

雙下劃線(__)
Python中,由雙下劃線開始的屬性在運行時被“混淆”,所以直接訪問是不允許的。實際上,會在名字前面加上下劃線和類名。比如,上例中self.__num屬性被“混淆”后,用于訪問這個數據值的標識就變成了self._NumStr__num.把類名加上后形成的新的“混淆”結果將可以防止在祖先類或子孫類中的同名沖突。
盡管這樣做提供了某種層次上的私有化,但算法處于公共域中并且很容易被“擊敗”。這更多的是一種對導入源代碼無法獲得的模塊或對同一模塊中的其他代碼的保護機制。
這種名字混淆的另一個目的是為了保護__XXX變量不與父類名字空間相沖突。如果在類中有一個__XXX屬性,它將不會被其子類中的__XXX屬性覆蓋。而如果父類中僅有一個XXX屬性,子類也定義了這個屬性,那么子類的XXX就會覆蓋父類的XXX。使用__XXX,子類的代碼就可以安全地使用__XXX,而不必擔心它會影響父類中的__XXX

單下劃線(_)
簡單的模塊級私有化只需要在屬性名前使用一個但下劃線字符。這就防止模塊的屬性用“from mymodule import *”來加載這個屬性。這是嚴格給予作用域的,所以這同樣適合于函數。
Python2.2中引進的新式類,增加了一套全新的特征,讓程序員在類及實例屬性提供保護的多少上擁有大量重要的控制權。經管Python沒有在語法上把private,protected,friend或protected friend 等特征內建在語言中,但是可以按你的需要嚴格地定制訪問權。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宾阳县| 左云县| 汉沽区| 贵州省| 海原县| 高陵县| 溧水县| 宣汉县| 故城县| 泗阳县| 华池县| 台东县| 洛隆县| 元江| 广州市| 兴宁市| 邓州市| 公安县| 崇州市| 肥东县| 房产| 镇江市| 万山特区| 韶山市| 伊吾县| 军事| 阜新市| 黄山市| 宾阳县| 祁门县| 宽城| 长宁县| 新郑市| 城市| 曲松县| 沁水县| 永新县| 会东县| 偏关县| 七台河市| 历史|