zoukankan      html  css  js  c++  java
  • 7 上下文管理器 魔术方法 鸭子类型

    1.上下文管理器

    2.模式方法

    1.上下文管理器

    1).python的上下文管理协议:包含__enter__() 、__exit__()

    2)上下文管理器:支持"上下文管理的协议"的对象(同时支持__enter__()、__exit__())

    3)with 语句操作上下文管理器对象

    with object as o:

            pass

    with后的object是上下文管理器的对象;o是__enter__()的返回值

    举例:在执行with里的语句之前,先执行__enter__(),再执行with中的语句,最后执行__exit__()-------即上下文管理器

    """
    python上下文管理的协议:包含__enter__() 、__exit__()
    上下文管理协议:支持"上下文管理的协议"的对象(同时支持__enter__()、__exit__())
    with 后面跟的是上下文管理器的对象
    """
    # 自定义上下文管理器---包含__enter__()、__exit__()
    class MyClass(object):
    
        def __enter__(self):
            print("enter is running...")
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("exit is running...")
    
    def main():
        # 实例对象
        obj = MyClass()
        with obj as o:
            print("with is running...")
    
    if __name__ == '__main__':
        main()
    """
    enter is running...
    with is running...
    exit is running...
    """

    4)常见的上下文管理器

    open(file)

    with open("上下文管理器.txt","w",encoding="utf8") as f:
        f.write("python")

    为什么open能够被with操作?看看open()的返回值包含哪些属性:

    f = open("上下文管理器.txt") # f就是一个上下文管理器
    print(dir(f))
    """
    ... ,'__enter__', '__eq__', '__exit__', ...
    """

    open()的返回值f是一个上下文管理器,所以可以被with操作。

    当然,不是上下文管理器,是不能被with操作的!如下:

    with 1 as o:
        pass
    """
        with 1 as o:
    AttributeError: __enter__
    """

    5)如果一个对象不是上下文管理器,怎么变成上下文管理器

    在继承的新的类中,添加__enter__() 、__exit__() 2个方法即可。

    # 一个对象不是上下文管理器,怎么变成上下文管理器: 继承,在新的类中添加2个方法
    class Base(object):
        pass
    
    class MyBase(Base):
        def __enter__(self):
            pass
        def __exit__(self, exc_type, exc_val, exc_tb):
            pass

    2.魔术方法:

    在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法

    1)__str__:

    • 当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印这个方法中return的数据
    • __str__方法需要返回一个字符串,当做这个对象的描写

    举例,不写__str__()方法(默认继承object的str方法,返回的是存储地址)

    class MyClass(object):
    
        def __init__(self,name):
            self.name = name
    
    
    def main():
        a = MyClass("zhangsan")
        print(a)
    
    if __name__ == '__main__':
        main()  # <__main__.MyClass object at 0x000001B57D0BEC88>

    重写__str__()

    class MyClass(object):
    
        def __init__(self,name):
            self.name = name
    
        def __str__(self):
            print("这是对对象的描述...")
            return self.name
    
    
    def main():
        a = MyClass("zhangsan")
        print(a)
    
    if __name__ == '__main__':
        main()  
    """
    这是对对象的描述...
    zhangsan
    """

    2).让自定义的类生成的对象(实例)能够使用运算符进行操作

    首先来看下list的相加:

    def main():
        a = [1, 2, 3]
        b = [4, 5, 6]
        return a + b
    
    if __name__ == '__main__':
       print(main()) # [1, 2, 3, 4, 5, 6]

    为什么list能够相加?因为list含有__add__属性

    def main():
        a = [1, 2, 3]
        b = [4, 5, 6]
        print(hasattr(a,"__add__")) # True
        return a + b
    
    if __name__ == '__main__':
       print(main())

     1)对象没有__add__属性,进行相加:报错

    class MyClass(object):
        pass
    
    def main():
        a = MyClass()
        b = MyClass()
        return  a + b
    
    if __name__ == '__main__':
        print(main()) # TypeError: unsupported operand type(s) for +: 'MyClass' and 'MyClass'

    对象添加__add__属性

    两个对象能不能相加,取决于该对象有没有__add__属性

    class MyClass(object):
        def __init__(self, value):
            self.value = value
    
        def __add__(self, other):
            return self.value + other.value # self 是a对象,other是b对象
    
    
    def main():
        a = MyClass(11)  
        b = MyClass(23)
        return  a + b
    
    if __name__ == '__main__':
        print(main())  # 34

    3)__del__:Python解释器释放实例对象,调用该方法------析构方法

    当删除一个对象时,Python解释器也会默认调用一个方法,这个方法为__del__()方法。在Python中,对于开发者来说很少会直接销毁对象(如果需要,应该使用del关键字销毁)。Python的内存管理机制能够很好的胜任这份工作。也就是说,不管是手动调用del还是由Python自动回收都会触发__del__方法执行

    ①创建多个对象的时候触发__del__方法,python解释器自动删除对象,释放内存

    # python解释器删除不再用的对象 ,释放内存
    class MyClass(object):
        def __init__(self):
            print("init is running...")
    
        def __new__(cls, *args, **kwargs):
            print("new is running...")
            instance = super().__new__(cls)
            return instance
        def __del__(self):
            print("del is running...")
    
    def main():
        a = MyClass()
        b = MyClass()
    
    if __name__ == '__main__':
        main()
        print("main() is running...")
    """
    new is running...
    init is running...
    new is running...
    init is running...
    del is running...
    del is running...
    main() is running...
    """

    ②del关键字手动删除

    当使用del 把内存的所有应用删除,立刻调用__del__方法

    class MyClass(object):
        def __init__(self):
            print("init is running...")
    
        def __new__(cls, *args, **kwargs):
         #创建对象 分配内存
    print("new is running...") instance = super().__new__(cls) return instance def __del__(self):
         #销毁对象
    print("del is running...") def main(): a = MyClass() b = MyClass() del a del b

    __del__用其他应用场景:在对象被销毁前,进行一些其他操作。

    4)多态   (面向对象的三大特征:封装、继承、多态)

    类型动态

    同样的方法,根据不同的对象,会有不同的行为结果。

    简单来说,多态就是在子类中覆写父类的方法。这样做的好处是同样名称的方法在不同的子类中会有不同的行为。比方说,动物里面包含很多不同种类的动物,如:猫,狗。。。,但是它们有相同的特性就是跑,我们可以使用相同的方法来访问它们。
    # 多态
    class MyClass(object):
    
        def music(self):
            print("myclsss music is running...")
    
    class AClass(MyClass):
        def music(self):
            print("Aclass music is running...")
    
    class BClass(MyClass):
        def music(self):
            print("Bclass music is running...")
    
    def func(obj):
        obj.music()  # 只要这个对象有music方法,就会正常调用
    
    if __name__ == '__main__':
        m = MyClass()
        a = AClass()
        b = BClass()
       #上面三个对象都有music方法,都可以正常调用该方法 func(a) func(b) func(m)
    """ Aclass music is running... Bclass music is running... myclsss music is running... """

    5)鸭子类型-----依赖的是方法()

    ①什么是鸭子类型?

    在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法"方法 (计算机科学)")和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。-- 摘自鸭子类型的维基百科

    ②鸭子类型有什么用?

    ③ 理解:动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。

    ④举例

    我们可以使用一个函数 canrun() 来访问不同 Animal 子类中的相同方法。但其实对于上面的 canrun() 函数来说,传入的参数并不一定需要是 Animal 类型的,只需要保证传入的对象有一个 run() 方法即可,如下面代码所示。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子----有run()方法
    class Animal(object):
        def run(self):
            print("animal is running...")
    
    class Cat(Animal):
        def run(self):
            print("cat is running...")
    
    class Dog(Animal):
        def run(self):
            print("dog is running...")
    
    class Person(object):
        def run(self):
            print("person is running...")
    
    def canrun(obj):   # 只要obj含有run()方法,就是个鸭子类型
        obj.run()
    
    if __name__ == '__main__':
        canrun(Animal())
        canrun(Cat())
        canrun(Person())
    """
    animal is running...
    cat is running...
    person is running...
    """

    ⑤怎么实现鸭子类型?

    可以封装一个函数,函数中特殊的参数,对参数作特殊的说明:参数必须是含有什么方法的对象,就是一个鸭子类型。----只要这个参数含有想要的特殊方法,就是鸭子类型。

    5)类方法的使用

    类方法时为了创建对象!!!

    # 类方法创建对象
    class Person(object):
    
        def __init__(self, first_name, last_name):
            self.first_name = first_name
            self.last_name = last_name
    
        @classmethod
        def teacher(cls,full_name):
            # 类方法,扩展一个创建对象的方式
            first_name, last_name = map(str, full_name.split(" "))
            obj = cls(first_name, last_name)
            return obj
    
    if __name__ == '__main__':
        # 普通方式创建对象
        zhangsan = Person("san", "zhang")
        print(zhangsan.first_name)
    
        # 类方法创建对象
        teacher = Person.teacher("si li")
        print(teacher.first_name)
        print(teacher.last_name)

    6) __getitem__: ----有__getitem__方法的对象才能用[]操作

    ①作用:凡是在类中定义了这个__getitem__ 方法,那么它的实例对象obj,可以像这样

    obj[key] 取值,当实例对象做obj[key] 运算时,会调用类中的方法__getitem__

    ②使用:一般如果想使用索引访问元素时,就可以在类中定义这个方法(__getitem__(self, key) )

    ③举例

    class MyClass(object):
    
        def __init__(self):
            self.items = [1, 2, 3]
    
        def __getitem__(self, item):
            return self.items[item]
    
    if __name__ == '__main__':
        myclass = MyClass()
        print(myclass[0])  # 1
        print(myclass[1])  # 2
        print(hasattr(myclass, "__getitem__"))  # True
    print(hasattr(list, "__getitem__"))  # True
    print(hasattr(tuple, "__getitem__")) # True

    7)__getattr__:  执行调用对象的属性,而对象无此属性时,会调用__getattr__方法。

    # __getattr__方法
    class Myclass(object):
        def __init__(self,name):
            self.name = name
    
        def __getattr__(self, item):
            return 1
    if __name__ == '__main__':
        A = Myclass("zhangsan")
        print(A.value) # 1     对象A并没有value属性,但是输出的是__getattr__的返回值

    不重写__getattr__方法,对象没有X属性而调用X属性时,会报系统内置的属性错误信息。

    class Myclass(object):
        def __init__(self,name):
            self.name = name
    
        # def __getattr__(self, item):
        #     return 1
    if __name__ == '__main__':
        A = Myclass("zhangsan")
        print(A.value)  # AttributeError: 'Myclass' object has no attribute 'value'

    也可以自定义报错信息,重写__getattr__方法。如下:

    __getattr__内置方法,没有属性时的异常抛出的大概设计思路。

    #自定义获取对象属性的报错信息
    class MyAttributionError(Exception):
        def __init__(self,msg = "属性不存在"):
            super().__init__(self)
            self.msg = msg
    
        def __str__(self):
            return self.msg
    
    class Myclass(object):
        def __init__(self,name):
            self.name = name
    
        def __getattr__(self, item):
            raise MyAttributionError
    
    if __name__ == '__main__':
        A = Myclass("zhangsan")
        print(A.value)
    """
        raise MyAttributionError
    __main__.MyAttributionError: 属性不存在
    """
  • 相关阅读:
    mysql基本操作
    http基础
    react进阶第一讲jsx
    react进阶第二讲——component
    WC2020游记
    数据库实验小结 Resource Manager
    latex 常用小结
    编程之美2013资格赛 水结
    百度电影推荐系统比赛 小结 ——记我的初步推荐算法实践
    10.10作业
  • 原文地址:https://www.cnblogs.com/ananmy/p/14094973.html
Copyright © 2011-2022 走看看