Python面向对象之多态、鸭子类型、魔法方法、内置方法、上下文管理器


一、多态

  1、即同一个事物的多种形态

  如动物:狗、猪

class Animal:
     pass

class Dog(Animal):
     pass

class Pig(Animal):
     pass

  2、多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象

# class Animal: # 统一所有子类的方法
#     def say(self):
#         print('动物基本的发声模拟。。。',end=' ')

# class Dog(Animal):
#     def say(self):
#         super().say()
#         print('汪汪汪')
#
# class Pig(Animal):
#     def say(self):
#         super().say()
#         print('哼哼哼')

# obj1=Dog()
# obj3=Pig()
 

# obj1.say()
# obj3.say()
 
# 定义统一的接口,接收传入的动物对象
# def animal_say(animal):
#     animal.say()

# animal_say(obj1)
# animal_say(obj3)

  3、鸭子类型

    在Python中推崇的是鸭子类型

    在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由“当前方法和属性的集合”决定

class Duck():
    def walk(self):
        print('I walk like a duck')
    def swim(self):
        print('i swim like a duck')

class Person():
    def walk(self):
      print('this one walk like a duck')
    def swim(self):
      print('this man swim like a duck')
'''Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有walk()和swim()方法,就可以正确的被调用。'''

二、oop相关内置函数

  1、isinstance(obj,cls):判断一个对象是否是某个类的实例(判断类型的方法)

# 示例程序:
class Foo(object):
    pass
obj = Foo()
print(isinstance(obj, Foo))     #True

  2、issubclass(sub,super):判断一个类是否是另一个类的子类

class F(object):
    pass

class Bar(F):
    pass
print(issubclass(Bar, F))

三、类中的魔法方法

  1、__str__:调用str函数或者print函数时自动执行,返回值作为显示内容。可以利用该函数来自定义,对象的是打印格式。如果`__str__`没有被定义,那么就会使用`__repr__`来代替输出 
这俩方法的返回值必须是字符串,否则抛出异常。

class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return "这是一个person对象 name:%s age:%s" %(self.name,self.age)
        pass   #
p=Person("aaaa",19)
print(p)

#这是一个person对象 name:aaaa age:19

  2、__del__ 当手动删除对象时立马执行,或是程序运行结束时也会自动执行。当对象在使用过程中,打开了不属于解释器额资源,例如:文件 网络端口(网络编程)就可使用

class FileTool:
    """该类用于简化文件的读写操作 """

    def __init__(self,path):
        self.file = open(path,"rt",encoding="utf-8")
        self.a = 10

    def read(self):
        return self.file.read()

    # 如果确定程序执行完。这个对象肯定不使用了 可以放心的关闭问文件
    def __del__(self):
        self.file.close()


tool = FileTool("a.txt")
print(tool.read())

  3、__call__ 执行时机:在调用对象时自动执行(即对象+括号)对象=类名()

class A:
    def __call__(self, *args, **kwargs):
        print("call run")
        print(args)
        print(kwargs)

a = A()
a(4,a="私我")
'''
call run
(4,)
{'a': '私我'}
'''

  4、__slots__  :该属性是一个类属性,用于优化对象内存占用

  字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示,实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。 在__slots__中列出的属性名在内部被映射到这个数组的指定下标上。 使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='aaa'
f1.age=18
print(f1.__slots__)        #['name', 'age']
#f1.y=2  报错
#print(f1.__dict__) #f1不再有__dict__  ,这里报错,归__slots__管,节省内存

  5、__format__:调用format函数时自动执行,用于定制对象的格式化输出,

#规定输出格式
date_dic={
    'ymd':'{0.year}:{0.month}:{0.day}',
    'dmy':'{0.day}/{0.month}/{0.year}',
    'mdy':'{0.month}-{0.day}-{0.year}',
}
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

    def __format__(self, format_spec):
        if not format_spec or format_spec not in date_dic:
            format_spec='ymd'
        fmt=date_dic[format_spec]
        return fmt.format(self)

d1=Date(2016,12,29)
print(format(d1))                    #2016:12:29
print('{:mdy}'.format(d1))           #12-29-2016

四、属性的getattr setattr 和delattr  getattribute

#点语法.操作对象属性时自动触发

getattr:使用点语法访问属性的时候如果属性不存在时才执行
setattr:用点语法设置属性(修改/添加)时触发它的执行
delattr:使用点语法删除属性时触发
getattribute: 该函数也是用来获取属性的:在获取属性时如果存在gqtattribute 则先执行该函数,如果没有拿到属性则继续调用getattr函数,如果拿到了则直接返回
class A:
    def __setattr__(self, key, value):# 设置
        # print(key)
        # print(value)
        print("__setattr__")
        self.__dict__[key] = value

    def __delattr__(self, item): # 删除
        print("__delattr__")
        print(item)
        self.__dict__.pop(item)
        pass
    def __getattr__(self, item):  # 获取属性
        print("__getattr__")
        return 1
    def __getattribute__(self, item):
        print("__getattribute__")
        # return self.__dict__[item]
        return super().__getattribute__(item)
a = A()
a.name = "cc"
# print(a.name)
'''
__setattr__
__getattribute__
__getattribute__
jack
'''
del a.name   
'''
__setattr__
__getattribute__
__delattr__
name
__getattribute__
'''

五、[ ] 的原理:

getitem 当你用中括号去获取属性时 执行
setitem  当你用中括号去设置属性时 执行
delitem 当你用中括号去删除属性时 执行
class A:
    def __getitem__(self, item):
        print("__getitem__")
        return self.__dict__[item]

    def __setitem__(self, key, value):
        print("__setitem__")
        self.__dict__[key] = value

    def __delitem__(self, key):
        del self.__dict__[key]
        print("__delitem__")

a = A()
# a.name = "cc"
a["name"] = "cc"
print(a["name"])
del a["name"]
'''
__setitem__
__getitem__
cc
__delitem__
'''

六、运算符重载:对象比较大小

  原本自定义对象无法直接使用大于、小于来进行比较,我们可自定义运算符来实现,让自定义对象也支持比较云算符。__gt__:大于比较、 def __It__:小于比较、  def__eq__:等于比较

class Student(object):
    def __init__(self,name,age,height):
        self.name=name
        self.age=age
        self.height=height
    def __gt__(self, other): # 比较大于
        # print(self)
        print(other)
        return "有点大"
    def __eq__(self, other): # 比较等于
        #print(self)
        print(other)
        if self.name == other.name and self.age == other.age:
            return  "姓名和年龄一样"
        else:
            return  "姓名和年龄不一样"
    def __lt__(self, other): # 比较小于
        if self.age<other:
            return "太小了"
s=Student("reso",20,185)
s1=Student("Jack",18,155)
# print(s.name,s1.age)
print(s>s1)             #有点大
#print(s
print(s==s1)         #姓名和年龄不一样

七、迭代器协议

  迭代器是指具__iter__和__next__的对象,为对象增加这两个方法来让对象变成一个迭代器

class MyRange:

    def __init__(self, start, end, step):
        self.start = start
        self.end = end
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        a = self.start
        self.start += self.step
        if a < self.end:
            return a
        else:
            return "停止"for i in MyRange(1, 5, 1):
    print(i)
    
'''
1
2
3
4
'''

八、上下文管理

  上下文可以理解为是一个代码区间,一个范围,例如 witho open  打开的文件仅在这个上下文中有效涉及的方法:

    enter: 表示进入上下文     exit :  表示退出上下文

  当执行with语句时 会先执行enter,当代码执行完毕后执行exit 或者代码遇到了异常会立即执行exit,并传入错误的信息。包含:错误的类型 错误的信息 错误的追踪信息

  __enter__ 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量

  __exit__ with中代码块执行完毕时执行

   

  内存泄漏:内存泄露的根本原因在于创建了某个对象,却没有及时的释放掉,直到程序结束前,这个未被释放的对象一直占着内存。如果所占内存量少就影响不大,如果量大那么就会直接把内存占满,导致程序被 kill 掉,这就是内存泄露。文件的输入输出、数据库的连接断开等,都是很常见的资源管理操作。但资源都是有限的,在写程序时,我们必须保证这些资源在使用过后得到释放,不然就容易造成资源泄露,轻者使得系统处理缓慢,重则会使系统崩溃。在 Python 中,对应的解决方式便是上下文管理器(context manager)。

  基于类的上下文管理器:

class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        # print('enter')
        self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True
    def __getattr__(self, item):
        return getattr(self.f,item)

with Open('test.txt','w') as f:
    print(f)
    f.write('aaaaaabbbbbccccc')
    #f.quit #否则抛出异常,交给__exit__处理
'''
执行顺序
# 当我们用 with 语句,执行这个上下文管理器时:
# 1. 方法`__init__()`被调用,程序初始化对象 open,
# 2. 方法`__enter__()`被调用,文件被打开,并且返回 open 对象赋予变量 f;
# 3. 字符串“aaaaaabbbbbccccc”被写入文件“test.txt”; 
# 4. 方法`__exit__()`被调用,负责关闭之前打开的文件流。
注意:另外,__exit__()方法中的参数 “exc_type, exc_val, exc_tb”,分别表示 exception_type、exception_value 和 traceback。当我们执行含有上下文管理器的 with 语句时,如果有异常抛出,异常的信息就会包含在这三个变量中,传入方法__exit__()

  对于__exit__;

class Foo:
    def __init__(self):
        print('__init__ called')        

    def __enter__(self):
        print('__enter__ called')
        return self
    # 在__exit__方法中捕获并输出异常信息 
    def __exit__(self, exc_type, exc_value, exc_tb):
        print('__exit__ called')
        if exc_type:
            print(f'exc_type: {exc_type}')
            print(f'exc_value: {exc_value}')
            print(f'exc_traceback: {exc_tb}')
            print('exception handled')
        return True # 异常处理后必须返回True

# 调用并手动抛出异常
with Foo() as obj:
    raise Exception('exception raised').with_traceback(None)

# 输出
# __init__ called
# __enter__ called
# __exit__ called
# exc_type: 
# exc_value: exception raised
# exc_traceback: 
# exception handled

  上下文管理器对数据库操作

class DBCM: 
   # 负责对数据库进行初始化,也就是将主机名、接口(这里是 localhost 和 8080)分别赋予变量 hostname 和 port;
    def __init__(self, hostname, port): 
        self.hostname = hostname 
        self.port = port 
        self.connection = None
   # 连接数据库,并且返回对象;
    def __enter__(self): 
        self.connection = DBClient(self.hostname, self.port) 
        return self
   # 负责关闭数据库的连接
    def __exit__(self, exc_type, exc_val, exc_tb): 
        self.connection.close() 
  
with DBCM('localhost', '8080') as db_client: 
代码...

  基于生成器的上下文管理器

    使用装饰器 contextlib.contextmanager,来自定义基于生成器的上下文管理器,用以支持 with 语句。

from contextlib import contextmanager

@contextmanager
def file_manager(name, mode):
    try:
        f = open(name, mode)
        yield f
    finally:
        f.close()
        
with file_manager('test.txt', 'w') as f:
    f.write('hello world')

'''函数 file_manager() 是一个生成器,当我们执行 with 语句时,便会打开文件,并返回文件对象 f;

当 with 语句执行完后,finally block 中的关闭文件操作便会执行。

注意:基于生成器定义的上下文管理需要使用装饰器 @contextmanager,不再使用生成器协议方法。'''