日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python的魔法函数

發布時間:2023/12/9 python 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python的魔法函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概要

  • 如何定義一個類
  • 類里通常包含什么
  • 各個部分解釋
  • 類是怎么來的
  • type和object的關系
  • 判斷對象的類型
  • 上下文管理器
  • 類結構

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com# 類名后面寫(object)這種寫法表示是新式類,不寫object是經典類,兩種的區別是多繼承的問題。 class OOP(object):# 定義類變量,類共享的,那么實例A修改以后實例B也會受到影響(修改不可比那對象感官上看不到修改,如果是可變類型比如列表,感官上就有影響了),# 所以類變量是定義共有的屬性通常不能修改,雖然技術上也可以修改。不用實例化也可以訪問類變量。var1 = "hello" # 不可變對象list1 = [] # 可變對象def __call__(self, *args, **kwargs):"""這個函數不是析構函數也不是構造函數,對象或者實例可以像函數一樣調用,就像執行普通函數一樣。這個如果不需要也不用寫。:param args::param kwargs::return:"""print("我是特殊函數 __call__")def __init__(self):"""構造函數,用于初始化類的實例,實例化對象的時候就會自動調用這個方法,比如把實例變量綁定到實例上。這里這個self其實有些特別,也就是實例化的時候會自動把實例對象傳遞進來,self就是實例對象。oop = OOP() 這里其實是 OOP(oop) 所以self就是實例oop,這個self也就是為了接受隱式傳遞進來的實例名稱除了在 __init__ 方法中定義的東西屬于實例自己之外也就是在內存中也是獨立的,其他的內容包括類變量、類里面的方法在內存中都只有一份所有實例公用。
    多說一句,其實實例化的時候先執行__new__方法,該方法調用__init__方法。
    """# 在 __init__ 方法里定義的變量是實例變量,實例獨享的。通過實例調用變量或者其他資源是先找實例本身,如果沒有就找類的。# 比如類變量和實例變量同名,你通過實例訪問這個變量時,它會給你返回實例變量的值。# 加 "_" 表示私有變量,不過使用 "_" 也可以通過某種方式直接訪問,所以要想使用嚴格的私有變量要使用 "__" 雙下劃線。self._Var2 = ""print("我是構造函數 __init__")def __del__(self):"""析構函數,當實例對象刪除時候調用,所以不需要設置參數,你也傳遞不了參數。它是在實例銷毀的時候自動調用的。對于python解釋器來說,它有垃圾回收機制,只要有實例存在解釋器就認為這個類被使用。如果你del這個實例,其實你刪除的是這個實例,也就意味著切斷了實例和類的關系,當解釋器發現某個類沒有被引用了就可以在內存中刪除了。實例保存的是指向類的指針,實例放在棧里,類放在堆里。基本數據類型放在棧里,非基本數據類型真實的數據都是在堆里,而這個變量名在棧里。這個方法你通常不用寫。"""print("我是析構函數 __del__")def myMethod(self):"""為什么類里面的每一個方法都是有self呢?因為我們調用的時候雖然是通過實例名稱來調用,但是實際上是OOP.myMethod(oop)這種形式,你發現它還是會自動把實例傳進來,這個self就是用來接收這個實例的。所以你在自己的方法里面可以訪問變量。實例 oop 本身沒有 myMethod()方法,之所以可以調用成功就是 OOP.myMonth(oop) 完成的。對于實例來說類里面的方法都是公用的,你實例化多個對象其實這些對象是多個,但是它們所公用的類方法和類變量在內存中也只有一份,哪個個實例調用這個方法那么這個self就是哪個實例。記住一句話實例調用方法都是對實例自身進程操作的。再說詳細點,2個針對于同一個類的實例,A實例操作一個方法會影響B實例么?顯然不能,這就是self必須存在的原因,在JAVA中也是一樣,只是它使用this。:return:"""print("自定義方法")# 設置成屬性用于獲取內部變量 @propertydef Var(self):return self._Var2# 設置成屬性形式賦值給內部變量,這個必須寫在@property下面 @Var.setterdef Var(self, value):self._Var2 = valuedef main():oop = OOP()oop1 = OOP()# 調用實例的 __call__函數 oop()# 通過屬性方式修改變量oop.Var = "world"print(oop.Var)# 判斷是否有某種屬性# print(hasattr(oop, "_var1"))# 修改不可變對象(類變量)print("通過實例oop查看類變量var1的值:", oop.var1)print("通過實例oop1查看類變量var1的值:", oop1.var1)print("通過實例oop修改類變量var1的值:改為100")oop.var1 = 100print("通過實例oop查看類變量var1的值:", oop.var1)print("通過實例oop1查看類變量var1的值:", oop1.var1)# 從值上看感覺類變量也屬于實例,因為修改不影響,下面打印id你就看出來了# print("通過實例oop查看類變量var1的內存地址:", id(oop.var1))# print("通過實例oop1查看類變量var1的內存地址:", id(oop1.var1))# print("通過實例oop修改類變量var1的值:改為100")# oop.var1 = 100# print("通過實例oop查看類變量var1的內存地址:", id(oop.var1))# print("通過實例oop1查看類變量var1的內存地址:", id(oop1.var1))# 修改類變量可變類型變量print("oop實例向list1添加一個元素A")oop.list1.append("A")print("oop的 list1 內容為:", oop.list1)print("oop1的 list1 內容為:", oop1.list1)if __name__ == '__main__':main()

    關于類里面的類變量和實例變量還需要在說一下

    #!/usr/bin/env python # -*- coding: utf-8 -*-class A:# 類變量a = 11def __init__(self, x, y):# 實例變量self.x = xself.y = ya1 = A(1, 2) # 這里你以為是修改的類A里面的a嗎?其實不是,為什么,看下面 a1.a = 100 """ 這樣定義其實會讓a1這個實例多一個bb屬性出來(新建到a1這個實例中), 所以上面那個a1.a 其實并不是A類里面的類變量a,而是屬于實例自己的 """ a1.bb = 200 print(a1.x, a1.y, a1.a, a1.bb) # 這也就是a1為什么會有bba2 = A(5, 6) # 這里的a值還是11,a2這個實例本身沒有a這個變量,但是它自己沒有就會向上查找,它的上一級就是A這個類 print(a2.x, a2.y, a2.a)A.a = 500 # 這樣才會修改類變量,修改之后實例化的對象(之前或者之后)理論上都受到影響 print(A.a) a3 = A(8, 9) print("實例a3的a: ", a3.a)print("實例a2的a: ", a2.a) # a1之所以不受影響是因為之前 a1自己定義了一個a屬性,這樣它自己就有a屬性,從而就不用去找類的a屬性了 print("實例a1的a: ", a1.a)

    類中其他特殊方法(也叫做魔法方法)

    __dic__

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comclass TestObj(object):# 類變量var1 = 999def __init__(self, name, age):self.name = nameself.age = ageself.list1 = ['A', 'B']self.set1 = (1, 2)self.dic1 = {"a": "a", "b": "b"}def main():TO = TestObj(name="Tom", age=23)print(TO.__dict__)TO.name = "張三"print(TO.__dict__)if __name__ == '__main__':main()

    作用:查看實例里面的屬性,鍵為屬性名,值為屬性值,那都包含哪些屬性呢? 所有 self.XXX 的都是屬性。而且類變量也算。它永遠返回的是實例當前的最新值。從輸出看到類變量并沒有輸出。

    其實這個屬性通過類也可以調用

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comclass TestObj(object):# 類變量var1 = 999def __init__(self, name, age):self.name = nameself.age = ageself.list1 = ['A', 'B']self.set1 = (1, 2)self.dic1 = {"a": "a", "b": "b"}def main():# TO = TestObj(name="Tom", age=23)# print(TO.__dict__)# TO.name = "張三"# print(TO.__dict__)# 通過類來調用print(TestObj.__dict__)if __name__ == '__main__':main()

    除了輸出類變量之外還有一些類本身的東西。

    __str__和__unicode__方法

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comclass TestObj(object):# 類變量var1 = 999def __init__(self, name, age):self.name = nameself.age = agedef main():TO = TestObj(name="Tom", age=23)# 打印實例print(TO)if __name__ == '__main__':main()

    輸出是對象的地址

    這是默認輸出,如果你想改變輸出就是通過__str__來設置的,__unicode__是python2的方法,在python3中使用__str__

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comclass TestObj(object):# 類變量var1 = 999def __init__(self, name, age):self.name = nameself.age = age# 定義__str__方法def __str__(self):return "我是TestObj"def main():TO = TestObj(name="Tom", age=23)# 打印實例print(TO)if __name__ == '__main__':main()

    作用:在類里定義這個方法,那么在打印對象時將輸出該方法的返回值。在python3中是__str__ 在python2中是__unicode__。這個方法用的最多的是在Django的module中。

    __getitem__、__setitem__和__delitem__方法

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comclass TestObj(object):# 類變量var1 = 999def __init__(self, name, age):self.dic1 = {}def __getitem__(self, item):print("__getitem__", item)return self.dic1[item]def __setitem__(self, key, value):print("__setitem__", key, value)self.dic1[key] = valuedef __delitem__(self, key):print("__deleteitem__", key)def main():TO = TestObj(name="Tom", age=23)TO["name"] = "Lucy"TO["age"] = 23print(TO.dic1)print(TO["name"])del TO["name"]if __name__ == '__main__':main()

    運行結果

    通過這三個方法可以讓操作實例跟操作字典一樣。至于使用場景我也不知道,目前沒有用到過。

    這些魔法方法屬于誰

    #!/usr/bin/env python # -*- coding: utf-8 -*-class Company(object):def __init__(self, employee_list):self.employee = employee_listdef __getitem__(self, item):return self.employee[item]company = Company(["Tom", "Lucy", "Lily"]) print(company[:2])for item in company:print(item)

    換成字典就不行了

    __getitem__可以讓我們增加一些更加方便的方式去操作對象,但是上面之所以不能切片了是因為字典本身不能切片,而且__getitem__里面的item是索引并不是鍵,所以當我們換成字典的時候就會出錯。其實這種魔法方法不是為類而創建的的也不是object才有的,而是一種可以豐富對類的操作的一種方式。

    上下文管理器

    #!/usr/bin/env python # -*- coding: utf-8 -*-""" 正常使用try語句我們是這樣的 """ def exe_try():dic1 = {"A": "2"}try:print(dic1["B"])except KeyError as e:# 拋出異常執行這里print("KeyError")else:# 不拋出異常執行這里print("沒有異常")finally:# 無論是否拋出異常最后都執行這里print("finished")# exe_try()""" 你是否想過一個問題,打開文件會拋出異常,通常打開文件后也需要關閉文件流,為什么用 with open()語句可以不用手動關閉文件流呢? 這就是上下文管理器 """class Sample:def __init__(self):# 首先執行這個方法print("__init__")def __enter__(self):# 然后會自動調用這個方法,可以理解為獲取資源print("__enter__")return selfdef __exit__(self, exc_type, exc_val, exc_tb):# 這個函數會自動調用,當跳出with語句的時候,目的是為了釋放資源print("__exit__")def toDo(self):print("to do something")# 這個用法是不是很像 with open()呢? with Sample() as sample:sample.toDo()""" __enter__和__exit__構成了上下文管理器 """

    #!/usr/bin/env python # -*- coding: utf-8 -*-""" 如何把上下文管理器更加簡化一下呢? """import contextlib@contextlib.contextmanager # 這個裝飾器把下面的函數包裝成上下文管理器,主要利用了yiele的特性 def myFun(arg1):print("begin", arg1) # 相當于 __enter__ 里面的代碼yield {} # 這里必須有個生成器print("finished") # 相當于 __exit__ 里面的代碼with myFun("AAA") as my:print("BBB")

    類是如何創建的

    一切皆對象,任何一個對象都可以找到它屬于什么類型,那么類也是對象,那類的類型是什么呢?

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com__metaclass__ = typeclass TestObj(object):def __init__(self, name, age):self.name = nameself.age = age# 定義__str__方法def __str__(self):return "我是TestObj"def main():TO = TestObj(name="Tom", age=23)print(type(TO))print(type(TestObj))if __name__ == '__main__':main()

    輸出

    類的類型就是type,也就是說類是通過type來創建的。到底是怎么創建的呢?

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comdef __init__(self, name, age):self.name = nameself.age = age""" TestObj 對象名稱 type() 函數 "TestObj" 類名稱 (object,) 繼承自哪里,可以為空 {} 方法名稱和方法,字典形式 方法就是上面定義的 """ TestObj = type("TestObj", (object,), {"__init__": __init__})def main():TO = TestObj(name="Tom", age=23)print(type(TO))print(type(TestObj))if __name__ == '__main__':main()

    現在知道為什么TestObj的type是type了吧。

    關于type和object

    type用來生成類,而類用來生成實例,所以我們通過type命令可以查看這個實例或者說是對象是由誰生成的也就是其類型

    a = 1 print(type(1), " 生成1") print(type(int), " 生成int") print(type(type), " 生成type")

    # 基類 class MyClass:pass# object是所有類都要繼承的類也就是最頂層的類,或者說所有類的父類,類的祖先print("MyClass的基類是:", MyClass.__bases__) print("type的基類是:", type.__bases__) print("object的type是:", type(object)) print("Object的基類是:", object.__bases__)

    type的基類是object, 而object的type是type,看起來是個環形。object是type的實例,而type又繼承自object,type也是自己的實例。在Python中
    一切為對象,list、str、int等都是對象,可能有人問這命名是類啊,沒錯它們是類但也是對象。這一點和JAVA有所區別。這些東西之所有是類但同時也是對象
    是因為他們都是type的實例。它們繼承自object但是它們也是type的實例,只有實例才可以叫做對象,否則它們就是類。

    type是自己的實例,object是type的實例,而type又繼承了object,str是type的實例同時繼承了objcet。

    判斷對象類型

    我們知道獲取對象類型通過type來查看。但是還有一個叫做isinstance,這兩個有什么區別呢?

    #!/usr/bin/env python # -*- coding: utf-8 -*-class A:passclass B(A):def test(self):print("Test")b = B()print(isinstance(b, B)) print(isinstance(b, A))print(type(b) is A)

    結果是type不認為實例b是A的類型,但是isinstance則任務b是A的類型,b是B的實例,而B繼承了A所以說b是A的類型也沒有錯。那他倆有什么區別呢?

    發現用 isinstance 和 type 得到的結果不同,因為type(b) 指向的就是B這個類,而A就是A這個類,顯然不相同,雖然B繼承自A,但是用 isinstance 就會得到相同的結果,它會追溯它判斷b是不是A這個類型,因為b是B的實例,而B繼承自A,所以結果為 True。

    說到底type不會認為子類的對象的類型是父類,而isinstance則會子類的對象也是父類的類型。

    多繼承時super的執行順序

    #!/usr/bin/env python # -*- coding: utf-8 -*-class A:def __init__(self):print("A")class B(A):def __init__(self):print("B")# 調用父類方法,Python2寫法# super(Son, self).__init__()# 調用父類方法,Python3簡化寫法super().__init__() #class C(A):def __init__(self):print("C")super().__init__() #class D(B, C):def __init__(self):print("D")super().__init__() ## 打印D的MRO看一下 print(D.__mro__)d = D()

    常規上我們說super是調用父類方法,沒錯但是不太嚴謹,它是調用MRO路徑尋找的下一個類的方法。當你使用單繼承的時候看不出來,
    當使用多繼承的時候就會看出來。

    __new__方法

    #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.comclass TestObj(object):def __init__(self, name, age):self.name = nameself.age = ageprint("__init__方法執行了")def __new__(cls, *args, **kwargs):"""該方法會在__init__之前調用.:return:"""print("__new__方法執行了")"""如果你的類里自己重寫了 __new__ 方法那么下面的這個返回必須寫,否則你定義的類將不會執行__init__方法。這個類是繼承自object,那么你其實并不知__new__原本具體都做什么,出于某些原因你必須要重寫__new__方法,當你的邏輯寫完之后,就要通過 object.__new__(cls) 來調用父類的__new__方法,完成初始化。"""return object.__new__(cls)def main():TO = TestObj(name="Tom", age=23)print(TO.name)if __name__ == '__main__':main()

    ?

    轉載于:https://www.cnblogs.com/rexcheny/p/9496570.html

    總結

    以上是生活随笔為你收集整理的Python的魔法函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。