魔術方法

魔術方法

屬性 含義
__name__ 類、函數、方法等的名字
__module__ 類定義所在的模塊名
__class__ 對象或類所屬的類
__bases__ 類的基類的元組,舒徐為他們在基類列表中出現的順序
__doc__ 類、函數的文檔字符串,如果沒有定義則為None
__mro__ 類的mro,class.mro()返回的結果保存在__mro__中
__dict__ 類或實例的屬性,可寫的字典

查看屬性

方法:__dict__,返回類或者對象的所有成員列表。dir函數就是調用__dir__()。如果提供__dir__(),則返回屬性的列表,否則會盡量從__dict__屬性中收集信息。

  • 如果dir([object])參數包含方法__dir__,該方法會被調用。如果參數不包含,該方法將會最大限度的收集參數信息。
  • 對于不同類型的對象有不同的行為:如果對象是模塊對象,列表包含模塊的屬性名;如果對象時類型或者類對象,列表包含類的屬性名,以及它的基類的屬性名;否則列表包含對象的屬性名,它的類的屬性名和類的基類的屬性名。

魔術方法的分類

  1. 創建和銷毀:__init__和__del__
  2. hash
  3. bool
  4. 可視化
  5. 運算符重載
  6. 可調用對象
  7. 上下文管理
  8. 反射
  9. 描述器
  10. 其他

hash

  • __hash__內建函數hash調用的返回值,返回一個整數。如果定義這個方法該類的實例就可hash
  • __eq__:對應==操作符,判斷2個對象值是否相等,返回bool值 __hash__方法只是返回一個hash值作為set的key,但是去重,先要用is判斷是不是同一個,然后再用__eq__來判斷是否值相等。 hash值相等,只是hash沖突,不能說明兩個對象是相等的。因此一般來說提供__hash__方法是為了作為set或者dict的key的,所以去重要同時是提供__eq__方法。 可hash對象必須提供__hash__方法,沒有的話,isinstance(p1,collections,HASHABLE)一定為False。
class Point():
	def __init__(self,x,y):
		self.x = x
		self.y = y
	def __repr__(self):
		return 'Point({},{})'.format(self.x,self.y)
	
	def __eq__(self,other):
		return self.x == other.x and self.y == other.y

a = Point(3,4)
b = Point(3,4)

print(a.__repr__(b))
print({a,b})

bool

內建(函數bool(),或者對象放在邏輯表達式的位置,調用這個函數返回布爾值。沒有定義__bool__(),就找__len__()返回長度,非0為真。如果__len__()也沒有定義,那么所有實例都返回真。

class A:
    pass

print(bool(A()))

class B:
    def __bool__(self):
        return False

print(bool(B))
print(bool(B()))

class C:
    def __len__(self):
        return 0

print(bool(A()))

可視化 方法|意義 ———-|———— __repr__ |內建函數repr()對一個對象獲取字符串表達。如果一個類定義了__repr__但沒有有__str__,那么再請求該類的實例的非正式的字符串標識是,也將調用__repr__ __str__ | str函數、內建函數format、print函數調用,需要返回對象的字符串表達 __bytes__ | bytes的時候,返回一個對象的bytes表達,即返回bytes對象

class A:
    def __init__(self):
        self.a ='a'
        self.b = 'b'
    def __eq__(self, other):
        return False

    def __repr__(self):
        return 'REPR:{}({},{})'.format(self.__class__.__name__,self.a,self.b)
    def __str__(self):
        return 'STR:{}({},{})'.format(self.__class__.__name__,self.a,self.b)

print(A())
print([A()])
print(([str(A())]))

運算符重載

operator模塊提供一下的特殊方法,可以將類的實例使用下面的操作付來操作:

運算符 特殊方法 含義
<,<=,==,>,>=,!= __lt__,__le__,__eq__,__gt__,__ge__,__ne__ 比較運算符
+,-,*, /,%,//,**,divmod __add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__ 算數運算符,位運算符也有對應的方法
+=,-=,*=,/=,%=,//=,**= __iadd__,__isub__,__imul__,__itruediv__,__imod__,__floordiv__,__ipow__
  • 練習:完成point類設計,實現判斷點相等的方法,病完成向量的加法
class Point:
		def __init__(self,x,y):
			self.x = x
			self.y = y
			
		def __eq__(self,other):
			return self.x == other.x and self.y == other.y
			
		def __add__(self,other):
			return Point(self.x + other.x,self.y + other.y)
			
		def __repr__(self):
			return 'Point({},{})'.format(self.x,self.y)
	
	p1 = Point(3,4)
	p2 = Point(5,9)
	p3 = Point(5,9)
	print(p1 + p2)
	print(p2 == p3,p2 is p3)
  • 運算符重載應用場景 在做大量連續的運算時,使用運算符這種數學上常見的表達方式非常簡單、方便。提供運算符重載,比直接提供加方法更加適合程序員的變成習慣。 int類中幾乎實現了所有操作符,可以作為設計類的參考。

容器相關方法

方法 意義
__len__ 內建函數len(),返回對象的長度(>=0的證書),其實即使把對象當做容器類型看,就如同list或者dict。bool()函數調用的時候,如果沒有__bool__()方法,就會查看__len__()方法是否存在,存在返回非0為真
__iter__ 迭代容器時調用,返回一個新的迭代器對象
__contains__ in成員運算符,沒有實現就調用iter方法遍歷
__getitem__ 實現self[key]訪問。序列對象,可以接受整數為索引,或者切片。對于set和dict,key就是可hash的類型??梢圆淮嬖谝lKeyError異常
__setitem__ 和getitem的訪問能累死,是設置值的方法
__missing__ 字典使用getitem調用時,key不存在執行此方法
  • 練習:將購物車改造成方便操作的容器類
class Item:
    def __init__(self,**kwargs):
        self._property = kwargs

    def __repr__(self):
        return 'name:{} $:{} color:{}'.format(*self._property.values())

class ShopCar:
    def __init__(self):
        self.items = []

    def additem(self,item:Item):
        self.items.append(item)

    def __iter__(self):
        return iter(self.items)

    def __len__(self):
        return len(self.items)

    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self,index, value):
        self.items[index] = value


myshopcar = ShopCar()
myphone = Item(name='sumsung',price=4898,color='black')
mycar = Item(name='meserati',price=288,color='red')

myshopcar.additem(myphone)
myshopcar.additem(mycar)
print(myshopcar[1])
# myshopcar[0] = None
for item in myshopcar:
    print(item)

print(len(myshopcar))
print(myshopcar.__dict__)
print(mycar.__dict__,myphone.__dict__)

可調用對象

Python中一切皆對象,函數也是一樣。

def foo():
	print(foo.__name__,foo.__name__)
foo()	#等價于foo.__call__()

函數就是對象,對象foo加上(),就是調用對象的__call__方法,在自定義類中定義該方法,那么類的實例就可以像函數對象一樣加個括號調用。

  • 練習:定義一個斐波那契數列的類,方便調用,計算第n項
class Fib:
	def __init__(self):
		self.seq = [0,1,1]
	
	def __len__(self):
		return len(self.seq)
		
	def __call__(self,count):
		if count < len(self):#此處的判斷是為了減少計算,當已經算過的時候就不再需要重新計算
			return self.seq[count]
		for n in range(len(self)-1,count):#使用實例數列長度來作為起點可以減少計算,不必每次都從頭算起。
			self.seq.append(self.seq[n-1]+self.seq[n])
		return self.seq[count]
		
	def __getitem__(self,index):
		if index < 0:
			raise IndexError('Wrong Index')
		if index > len(self):#此處的判斷是如果需要的項沒有計算出來就立即計算
			return self(index)
		return self.seq[index]
		
	def __iter__(self):
		return iter(self.seq)

上下文管理

文件IO操作可以對文件對象使用上下文管理,使用with…as語法。

with open('test') as f:
	pass
  • 上下文管理對象:當一個對象同事實現了__enter__()和__exit__()方法,他就屬于上下文管理的對象
方法 意義
__enter__ 進入與此對象相關的上下文。如果存在該方法,with語法會把該方法的返回值作為綁定到as子句中指定的變量上
__exit__ 退出與此對象相關的上下文。
class Point:
    def __init__(self):
        print('init')
    def __enter__(self):
        print('enter')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:
    print("let's do it")

根據上例發現,實例化對象的時候,并不會調用enter,當進入with語句塊調用enter方法,然后執行語句塊,最后退出上下文管理時,調用exit方法。由此表明,with可以開啟一個上下文運行環境,在執行前做一些操作,執行后做一些收尾工作。

  • 上下文管理的安全性 分別看下異常和極端情況下對下上下文管理的影響
import sys

class Point:
    def __init__(self):
        print('init')
    def __enter__(self):
        print('enter')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:
    raise Exception('error')
    # sys.exit() #sys模塊方法,可直接退出當前解釋器
    print("let's do it")
print('outer le')

上個例子的返回值中觀察發現,不管是報錯還是強制退出解釋器,enter和exit方法都會執行,說明上下文管理很安全

  • enter和exit方法
    1. enter方法沒有其他參數,他的返回值就是上下文中使用的對象,with語法會把他的返回值賦給as子句的變量。
    2. exit方法有3個參數,exctype、excvalue、tracback,這三個參數都與異常有關,分別是異常類型、異常的返回值、異常的追蹤消息。
    3. exit方法返回一個等效True的值,則壓制異常;否則繼續拋出異常
  • 練習:使用上下文管理顯示加法函數的執行時長
import datetime,time

class TimeIt:
	
	def __init__(self,func):
		self._fn = func
	
    def __enter__(self):
        self.start = datetime.datetime.now()
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print((datetime.datetime.now() - self.start).total_seconds())

def add(x,y):
    time.sleep(3)
    return x+y

with TimeIt() as f:
    print(add(7,9))
  • 如果要求寫成如下形式,代碼要如何修改
with TimeIt(add) as foo:
	foo(4,5)

實現如下:

import datetime,time

class TimeIt:
	
	def __init__(self,func):
		self._fn = func
	
	def __call__(self,x,y):
		return self._fn(x,y)
			
    def __enter__(self):
        self.start = datetime.datetime.now()
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        print((datetime.datetime.now() - self.start).total_seconds())

def add(x,y):
    time.sleep(3)
    return x+y

with TimeIt() as foo:
    print(foo(7,9))

根據以上代碼,將類當做裝飾器來裝飾函數

import datetime,time
from functools import wraps

class TimeIt:
	
	def __init__(self,func):
		self._fn = func
		wraps(func)(self)    #復制add的函數該有的屬性到實例add,等價于@warps(func)	
    def __enter__(self):
        self.start = datetime.datetime.now()
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print((datetime.datetime.now() - self.start).total_seconds())
    
    def __call__(self,x,y):    #使實例可調用的魔法方法
	    return self._fn(x,y)

@TimeIt    #等價于add = TimeIt(add)
def add(x,y):
	"""This is a add function"""
    time.sleep(3)
    return x+y

add(4,5)
print(add.__dict__)
  • 上下文應用場景
    1. 增強功能:在代碼執行的前后增加代碼,以增強其功能。類死裝飾器的功能
    2. 資源管理:打開的資源需要關閉,例如文件對象、網絡連接、數據庫連接等
    3. 權限驗證:在執行代碼前,在enter方法中做權限的驗證
  • context.contextmanager 它是一個裝飾器,裝飾一個函數也實現了上下文管理,而且不用像類一樣實現enter和exit方法。 對下面的函數有要求,必須有yield,也就是這個函數必須返回一個生成器,且只有yield一個值。
import contextlib

@contextlib.contextmanager
def foo():
    print('enter')
    yield #yield的值只能有一個,作為enter方法的返回值
    print('exit')

with foo() as f:
	#raise EXCEPTION
    print(f)

上面的代碼看起來很正常,當時增加一個異常后發現不能保證exit的執行。

import contextlib

@contextlib.contextmanager
def foo():
    print('enter')
	try:
	    yield #yield的值只能有一個,作為enter方法的返回值
    finaly:
	    print('exit')

with foo() as f:
	raise EXCEPTION
    print(f)

當增加了try\finaly語句之后發現exit又可以執行。在yield發生處為生成器函數增加了上下文管理。

  • 總結:如果業務邏輯簡單可以使用函數加裝飾器方式,如果業務復雜,用類的方式加enter和exit更加方便。

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88766

(0)
KX_ilKX_il
上一篇 2017-11-20
下一篇 2017-11-21

相關推薦

  • 1. 什么是Linux

        如果以前從沒有接觸過linux, 你可能會對為什么會存在這么多不同的linux發行版有些困惑. 在看linux軟件包時, 你肯定聽過發行版, LiveCD和GNU之類的等等術語, 也肯定摸不著頭腦. 第一次接觸linux,想理解會有些困難.  我們就先了解下linux系統內部結構的一些信息. &nbs…

    Linux干貨 2016-10-26
  • 文本處理(1)

    文本處理工具最全整理上半部

    Linux干貨 2018-03-15
  • HAProxy

    LB Cluster: 四層:lvs, nginx(stream),haproxy(mode tcp) 七層:http: nginx(http, ngx_http_upstream_module), haproxy(mode http), httpd, ats, perlbal, pound… HAProxy 程序環境: 主程序:/usr/sbi…

    Linux干貨 2017-07-03
  • Linux基礎—權限、grep使用

    今天主要是幾個練習,內容如下: 1、復制/etc/skel目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其他用戶均沒有任何訪問權限 [root@localhost ~]# cp -R /etc/skel/ /home/tuser1/ [root@localhost …

    Linux干貨 2016-10-10
  • LVS實現

    一 LVS-NAT實驗前的準備 操作系統:CentOS 6.7 64位 配置防火墻,iptables –F 清理防火墻規則或者關閉iptables 關閉SELINUX, setenforce 0  #立即生效(實際是寬容模式) Director ip:172.16.2.1  VIP:192.168.1.8 RS1 ip:172.16.2.…

    Linux干貨 2016-12-29
  • 精解局域網訪問及共享(三)

    原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://jeffyyko.blog.51cto.com/28563/155109    上一節中的2種情況都是默認的簡單共享,下面我們著重分析一下高級共享方式下的各種訪問情況。畢竟這種方式用的很普遍,而且在權限…

    Linux干貨 2015-03-25
欧美性久久久久