面向对象高级(下)

1 item方法

  想对比__getattr__(), __setattr__() 和 __deltattr__()这三个通过属性的方式的三个方法

  还有__getitem__(), __setitem__() 和 __delitem__()这三个函数, 是通过字典形式来处理属性

  字典形式使用中括号的方式获取值

  在实现__setitem__()的时候仍然需要使用__dict__来实现增值的设置

  具体的用法如下所示

class People:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print("==>get")
        print(self.__dict__[item])

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

    def __delitem__(self, key):
        print("==>del")
        self.__dict__.pop(key)

whc = People('whc')

whc['age'] = 18
whc['age']
del whc['age']

2 __str__

  __str__() 和 __repr__()两个方法是用于输出信息的方法  

  和__iter__()方法一样, 对象调用的时候可以直接用 str(对象) 或者 repr(对象) 来对应的调用__str__()和__repr__()

  当使用 str函数 或者是print的时候, 调用__str__()

  当使用 repr函数 或者直接在交互式环境的时候, 调用__repr__()

  如果没有__str__就会使用__repr__代替

  由于一般这俩效果相同, 因而一般先定义__str__, 然后写上__repr__ = __str__

  这俩的返回值必须是字符串

  和他俩相关的是__format__(), 当调用 format() 方法的时候会调用

  具体操作代码如下

class Space:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'Space(%s,%s)' %(self.name,self.addr)

    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        return 'Space({},{},{})'.format(self.name, self.addr, self.type)

space = Space('兴业家园', '北京', '公寓')
print( repr(space) )
print( str(space) )
print( space )
print(format(space))

3 __slots__

  python中对对象可以任意绑定属性, 但是如果需要限制属性的绑定的时候就可以在类中定义一个__slots__

  它的值一般赋值成一个元组, 里面的写上可以绑定的属性的名字

  当绑定别的名字的时候就会报错

  __slots__的实现细节是将对象的__dict__取消了, 而分别对每个对象指定一个__slots__

  具体使用代码如下

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

whc = People()
whc.name = 'whc'
print(whc.__slots__)

fizz = People()
fizz.name = 'fizz'
print(fizz.__slots__)

4 __iter__

  可迭代对象是有方法__iter__()

  迭代器是有方法__next__()

  因而可以自己构建一个类, 使得它的对象, 既是一个可迭代对象, 也是一个迭代器  

  具体仿造range方法实现的代码如下

class MyRange:
    def __init__(self, *args):
        if len(args) == 1:
            self.start = 0
            self.end = args[0]
        else:
            self.start = args[0]
            self.end = args[1]
        if len(args) == 3:
            self.len = args[2]
        else:
            self.len = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.start >= self.end:
            raise StopIteration
        n = self.start
        self.start += self.len
        return n

for i in  MyRange(11):
    print(i)

for i in  MyRange(2, 11):
    print(i)

for i in  MyRange(2, 11, 3):
    print(i)

5 __doc__

  在类中最开始定义的光秃秃的字符串是类的文档信息

  查看该文档信息可以使用__doc__来获取

  在函数中的第一个光秃秃的字符串也是, 通过函数名来调用__doc__即可获得

  另外__doc__无法被继承

  具体代码如下

class Foo:
    '我是Foo的描述信息'
    def foo(self):
        '我是foo()的描述信息'
        pass

print(Foo.__doc__)
print(Foo().__doc__)

print( Foo.foo.__doc__ )
print(Foo().foo.__doc__ )

6 __module__

  __module__和__class__是两个特殊属性

  可以通过对象点的方式获取

  __module__是获取对象所在模块的名字. __class__是获取对象所在的类

  具体代码如下

import time
print(time.time.__module__)
print(time.time.__class__)
# time
# <class 'builtin_function_or_method'>

7 __del__

  这是一个特殊地函数--析构函数

  调用内置方法del的时候可能会调用到类中__del__()方法

  是否执行析构函数的判定是, del后面的变量指引的内容的指引数是否大于0, 如果不大于0则就会调用

  在程序结束之后, 如果有没有清除的变量, 对应的__del__()也会执行

  具体实例如下

class Foo:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print('{} 执行我啦'.format(self.name))

f0 = Foo('f0')
del f0 #执行__del__

f1 = Foo('f1')
f2 = f1
del f1 #由于还有f2这个指向, 所以不执行__del__

f3 = Foo('f3') #由于此处再新建了一个, 结束了之后这个也要__del__
# f0 执行我啦
# f1 执行我啦
# f3 执行我啦

8 __enter__

  在文件操作中, 有个with操作, 语句块结束后可以自动关闭文件  

  具体可以模拟文件操作来实现open()的功能

  with语句的流程如下

  with后的操作会生成一个对象, 此时后调用__init__()方法

  通过as需要返回给一个对象给as后的变量, 此时是执行__enter__()方法, 返回值给该变量

  在执行完毕with内部的代码块后, 会再执行__exit__()方法

  __exit__自带三个额外的参数, 分别是异常类型, 异常值, 异常的堆栈信息

  __exit__可以有返回值, 但是会判定成布尔值, 为Ture之后, with代码块出现异常的语句之后的语句不再执行, 但是with之后的语句会继续执行

   具体顺序的验证如下

class Foo:
    def __enter__(self):
        pass
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("====>",exc_type, exc_val, exc_tb, "<=====")
        return True

with Foo() as f:
    raise TypeError("出现错误")
    print("***************")

print("==> ######## <==")
# ====> <class 'TypeError'> 出现错误 <traceback object at 0x0000000000D4F1C8> <=====
# ==> ######## <==

  完成自定义的open()操作的类的实现代码如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'weihuchao'


import time
class LogFile:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)

    def write(self,line):
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    def __getattr__(self, item):
        return getattr(self.file,item)

with LogFile('log', 'w+') as f:
    f.write("aaa")
    f.seek(0)
    print(f.read())

9 __call__

  在类中还可以定义一个特殊函数__call__()来实现对象的可调用属性

  具体实现是

class Foo:
    def __call__(self, *args, **kwargs):
        print('__call__')
        
obj = Foo()
obj()

10 元类

  对象是由类产生的, 类是由type产生的, type继续由type产生

  type()手动创建类需要三个元素,

    一个是字符串类型, 表示类名

    一个是元组类型, 表示父类的集合

    一个是字典类型, 表示绑定的属性和方法

  具体实现如下

def run(self):
    print("running..")

PeopleClass = type("People",(object,), {"country":"China",'run':run})
whc = PeopleClass()
print(PeopleClass)
print(whc)
# <class '__main__.People'>
# <__main__.People object at 0x0000000000B27CC0>

10.1 利用元类新增功能

  可以写一个元类Mateclass, 它需要继承自type类

  原来的类需要关联该元类, 也就是在继承中有 metaclass=元类名字

  此时执行元类就可以生成一个对象, 也就是创建的这个类

  基于此, 就是元类中的__init__()方法创建的 类对象, 所以新加的功能只需在__init__()方法中就行

  元类的__init__()有额外三个参数, 分别是类名, 类基类, 类属性字典

  实现检查__doc__的代码如下

class MyMetaclass(type):
    def __init__(self, class_name, class_bases, class_dic):
        for key, value in class_dic.items():
            if callable(value) and not value.__doc__:
                raise Exception("{}方法内必须写入注释..".format(key))

class Foo(metaclass=MyMetaclass):
    x = 1
    def __init__(self, name):
        self.name = name
    def run(self):
        'run function'
        print('running')
    def go(self):
        print('going')

10.2 新建对象的过程

  首先, 类要生成对象, 类本身需要可调用

  针对于基类, 类是基类的对象, 也就是说在基类中, 需要有一个__call__()函数, 而这个函数, 是在类的__init__之前执行的

  在基类的__call__()方法中, 需要使用self.__new__(self)来创建一个空对象, 这个对象就是类

  有了类之后就可以调用原有的方法__init__(), 这时在其中就是熟悉的生成对象了

  最后再返回这个类就行了

  完成整个过程的代码如下

class MyMetaclass(type):
    def __call__(self, *args, **kwargs):
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)  # obj.name='egon'
        return obj

class People(metaclass=MyMetaclass):
    def __init__(self, name):
        self.name = name

whc = People('whc')
print(whc.name)

  

人若有恒 无所不成
原文地址:https://www.cnblogs.com/weihuchao/p/6762521.html