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: 属性不存在
"""