Python——魔術方法

特殊屬性

__name__    類、函數、方法等的名字

__module__  類定義所在的模塊

__class__   對象或類所屬的類

__bases__   類的基類的元組,順序爲他們在基類列表中出現的順序

__doc__     類、函數的文檔字符串,如果沒有定義則爲None

__mro__     類的面容,class.mro()返回的結果 保存在__mro__中

__dict__    類或實例的屬性,可寫的字典

查看屬性

__dir__     返回類或者對象的所有成員名稱列表。dir() 函數就是調用__dir__()。

使用實例調用時,如果提供__dir__(),則返回其返回值,要求是可迭代對象

           如果沒有提供__dir__(),則會從實例和類及祖先類中手機信息

如果dir([obj])  參數obj包含方法 __dir__(),該方法將被調用。如果Obj 不包含 __dir__(),該方法將最大限度收集屬性信息

dir(obj) 對於不同類型的對象obj具有不同的行爲:

1.如果對象是模塊對象,返回的列表包含模塊的屬性名和變量名

2.如果對象是類型或者類對象,返回的列表包含類的屬性名,及它的基類的屬性名

3.如果obj不寫  即dir(),返回列表包含內容不同

- 在模塊中,返回模塊的屬性和變量名

- 在函數中,返回本地作用域的變量名

- 在方法中,返回本地作用域的變量名

dir()測試如下:

class Person:

   def show(self):

       a = 100

       t = int(a)

       print(dir())

def test(a=50, b=100):

   print(dir())

Person().show()

test()

-----------------------------------------------

['a', 'self', 't']

['a', 'b']


魔術方法***

分類:

1.創建、初始化與銷燬

__new__ 、__init__ 與__del__

2.hash

3.bool

4.可視化

5.運算符重載

6.容器和大小

7.可調用對象

8.上下文管理

9.反射

10.描述器

11.其它雜項

實例化

__new__方法很少使用,即使創了該方法,也會使用 return super().__new__(cls)基類obj 的 __new__方法來創建實例並返回

class A:

   def __new__(cls, *args, **kwargs):

       print(cls)

       print(*args)

       print(**kwargs)

       return super().__new__(cls) # 返回cls的實例

       # return 1

       # return None

   def __init__(self, name):

       self.name = name

a = A("tom")

print(a)

--------------------------------------------

<class '__main__.A'>

tom

<__main__.A object at 0x00000281924C46A0>


hash

hash(x) x都一樣,求得hash應該不變的,冪等性:一般來說,x不一樣,hash應該不一樣.

不同的hash算法都有hash衝突的問題,即不同的x求得同樣的hash值

hash值相同就會去重嗎??

不是的

list 類爲爲什麼不可hash ??

源碼中有一句 __hash__ = None, 也就是調用  __hash__() 相當於 None(),一定報錯

所有類都繼承object,而在這個類是具有 __hash__() 方法的,如果一個類不能被hash,就把__hash__設置爲 = None

class A:

   def __init__(self, name, age=18):

       self.name = name

   def __hash__(self):

       return 1

   # def __eq__(self, other):

   #     return self.name == other.name

   def __repr__(self):

       return self.name

print(hash(A("tom")))

print((A('tom'), A('tom')))

print([A('tom'), A('tom')])

print('-------------------------')

s = {A('tom'), A('tom')}  # set

print(s)

print({tuple('t'), tuple('t')})

print({('tom',), ('tom',)})

print({"tom", "tom"})

---------------------------------------------------------------

1

(tom, tom)

[tom, tom]

-------------------------

{tom, tom} # set 沒有去重

{('t',)}

{('tom',)}

{'tom'}

__hash__方法只是返回一個hash值作爲set 的key,但是去重, 還需要__eq__ 來判斷兩個對象是否相等。

hash值相等,只是hash衝突,不能說明兩個對象是相等的。

因此,一般來說提供__hash__方法是爲了作set或者dict的key,所以 去重,要同時提供__eq__方法

不可hash對象 isinstance(p1, collection.Hashable) 一定爲False(import collections)

去重需要提供 __eq__方法

class A:

   def __init__(self, name, age=18):

       self.name = name

   def __hash__(self):

       return 1

   def __eq__(self, other): # 提供了__eq__方法

       return self.name == other.name

   def __repr__(self):

       return self.name

print(hash(A("tom")))

print((A('tom'), A('tom')))

print([A('tom'), A('tom')])

print('-------------------------')

s = {A('tom'), A('tom')}

print(s)

print({tuple('t'), tuple('t')})

print({('tom',), ('tom',)})

print({"tom", "tom"})

------------------------------------------------

1

(tom, tom)

[tom, tom]

-------------------------

{tom} #去重了

{('t',)}

{('tom',)}

{'tom'}


bool

可視化

注意:類型判斷要使用type或isinstance, 不能通過判斷print輸出是否帶引號來判斷輸出值的類型。

class A:

   def __init__(self, name, age=18):

       self.name = name

       self.age = age

   def __repr__(self):

       return "repr:{},{}".format(self.name, self.age)

   def __str__(self):

       return "str:{},{}".format(self.name, self.age)

   def __bytes__(self):

       return "{} is {}".format(self.name, self.age).encode()

print(A('tom'))

print([A('tom')])

print([str(A('tom'))])

print(bytes(A('tom')))

print('str:a,1')

s = '1'

print(s)

s1 = 'a'

print(s1)

print([s1], (s,))

print({s, "a"})

-----------------------------------------------------------------

str:tom,18

[repr:tom,18]

['str:tom,18']

b'tom is 18'

str:a,1

1

a

['a'] ('1',)

{'1', 'a'}


運算符重載

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

實現A類的2 個實例相減

class A:

   def __init__(self, name, age=18):

       self.name = name

       self.age = age

   def __sub__(self, other):

       return self.age - other.age

   def __isub__(self, other):

       # self.age -= other.age

       # return self

       # return self.__clas__(self.name, self - other)

       return A(self.name, self - other)

tom = A('tom')

jerry = A('jerry',16)

print(tom - jerry)

print(jerry - tom, jerry.__sub__(tom))

print(id(tom))

tom -= jerry

print(tom.age, id(tom))

-------------------------------------------------------

2

-2 -2

1864834369800

2 1864834369800

__isub__方法定義,一般會in-place來修改自身

如果沒有定義 __isub__方法,則會調用__sub__


運算符重載應用場景

往往是用面向對象實現的類,需要做大量的運算,而運算符時這種運算在數學上最常見的表達方式,例如 對 - 進行了運算符重載,實現了類 的二元操作,重新定義爲 tom - jerry

@functools.total_ordering裝飾器

__lt__, __le__,__eq__,__gt__,__ge__是比較大小必須實現的方法,但是全部寫完太麻煩了,,使用@functools.total_ordering裝飾器就可以大大簡化代碼

但是要求__eq__必須實現,其他方法__lt__,__le__,__gt__,__ge__ 實現其中一個

__eq__ 等於 可以推斷 不等於

__gt__ 大於 可以推斷 小於

__ge__ 大於等於 可以推斷 小於 等於

也就是用3 個方法,就可以吧所有比較解決了,所以total_ordering可以不使用

class Point:

   def __init__(self, x, y):

       self.x = x

       self.y = y

   def __hash__(self):

       return hash((self.x, self.y))

   def __eq__(self, other):

       return self is other or (self.x == other.x and self.y == other.y)

   def __add__(self, other):

       return Point(self.x + other.x, self.y + other.y)

   def __iadd__(self, other):

       self.x, self.y = self.x + other.x, self.y + other.y

       return self

   def __eq__(self, other): #判斷 == , !=

       return self.x == other.x

   def __gt__(self, other): # 判斷> , <

       return self.x > other.x

   def __ge__(self, other): # 判斷>= , <=

       return self.x >= other.x

a = Point(3,2)

b = Point(1,3)

c = Point(3,1)

print(a + b)

print(a > b)

print(a > c)

print(a < b)

print(a < c)

print(a >= c)

print(a <= c)

-----------------------------------------------------------

<__main__.Point object at 0x000001F51ACA49E8>

True

False

False

False

True

True

容器相關方法

爲什麼空字典,空字符串,空元祖,空列表,空集合可以等效爲False

因爲 __bool__未定義時,回調用 __len__ 結果爲0 等下 False

class A(dict):

   def __missing__(self, key):

       print('Missingn key:', key)

       return 0

a = A()

print(a['k'])

------------------------------

Missingn key: k

0

可調用對象

Python 中一切皆對象,函數也不例外

函數即對象,對象foo加上(), 就是調用此函數對象的__call__()方法

def foo():

   print(foo.__module__, foo.__name__)

foo()

# 等價於

foo.__call__()

可調用對象: 定義一個類,並實例化的到其實例,將實例像函數一樣調用

class Point:

   def __init__(self, x, y):

       self.x = x

       self.y = y

   def __call__(self, *args, **kwargs):

       return "<Point {}:{}>".format(self.x, self.y)

p = Point(4, 5)

print(p)

print(p())

--------------------------------------------------------

<__main__.Point object at 0x000001F7E8E30A20>

<Point 4:5>

class Adder:

   def __call__(self, *args):

       ret = 0

       for x in args:

           ret += x

       self.ret = ret

       return ret

adder = Adder()

print(adder(4, 5, 6))

print(adder.ret) # 當adder(4,5,6)不存在時,__call__方法不會調用,Adder無adder.ert屬性,會拋異常

-------------------------------------------------------

<Point 4:5>

15

15


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章