Python从入门到精通之面向对象进阶版

isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls): 检查obj是否是类 cls 的对象
issubclass(sub, super): 检查sub类是否是 super 类的派生类

反射

概念
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
表现为四个可以实现自省的函数,适用于类和对象(一切皆对象,类本身也是一个对象):
1. hasattr(object, name): 判断object中有没有一个name字符串对应的方法或属性
2. getattr(object, name, default=None): 获取object中的name属性并返回,若没有,返回defalt的值
3. setattr(object, key, val): 设置object的key属性的值为val,相当于 object.key = val
4. delattr(object, key): 删除object的key属性

自省函数的用法

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

f = Foo('lee')
# 检测是否含有某属性
print(hasattr(f,'name')) # True
print(hasattr(f,'show_name')) # True

# 获取属性
name = getattr(f, 'name')
print(name) # lee
func = getattr(f, 'show_name')
func() # lee
print(getattr(f,'age','找不到此属性!')) # 找不到此属性!

# 设置属性
setattr(f,'age', 24)
setattr(f,'show_name', lambda self:print('Hello %s'%self.name))
f.show_name(f) # Hello lee

# 删除属性
delattr(f,'name')
delattr(f,'show_name')
delattr(f,'sex') # 不存在则报错

注意: 类其实也是对象,上述方法对类一样有效。

__setattr__、__delattr__和__getattr__

class Foo:
    x = 1

    def __init__(self, y):
        self.y = y

    def __getattr__(self, item): # 只有属性未找到时触发
        print('---> from __getattr__: property [%s] is not founded'%item)

    def __setattr__(self, key, value):
        print('---> from __setattr__')
        # self.key = value    # Wrong!
        self.__dict__[key] = value

    def __delattr__(self, item):
        print('---> from __delattr__')
        # del self.item # Wrong!
        self.__dict__.pop(item)


# add/modify property will call the __setattr__ method automatica1ly
f = Foo(10)
print(f.__dict__)
f.z = 3
print(f.__dict__)

f.__dict__['a'] = 3
print(f.__dict__)
del f.a
print(f.__dict__)

print(f.a)  # __getattr__
print(f.x)

__getattribute__

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

    def __getattribute__(self, item):
        print('---> from __getattribute__')

    def __getattr__(self, item):
        print('---> from __getattr__')

f = Foo(10)
f.x
f.y
# 当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

描述符

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。
__get__(): 调用一个属性时触发
__set__(): 为一个属性赋值时触发
__delete__(): 采用del删除一个属性时触发
定义一个描述符:

class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)。

class Foo:
    def __get__(self, instance, owner):
        print('---> from __get__')

    def __set__(self, instance, value):
        print('---> from __set__')

    def __delete__(self, instance):
        print('---> from __delete__')


f = Foo()
f.name = 'lee'
f.name
del f.name
# 包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法,但何时何地才会触发呢?

描述符的正确姿势:

# Str
class Str:
    def __get__(self, instance, owner):
        print('---> from Str :: __get__')

    def __set__(self, instance, value):
        print('---> from Str :: __set__')

    def __delete__(self, instance):
        print('---> from Str :: __delete__')


# Int
class Int:
    def __get__(self, instance, owner):
        print('---> from Int :: __get__')

    def __set__(self, instance, value):
        print('---> from Int :: __set__')

    def __delete__(self, instance):
        print('---> from Int :: __delete__')


class People:
    name = Str()
    age = Int()

    def __init__(self, name, age):
        self.name = name
        self.age = age


lee = People('lee', 24)

lee.name
lee.name = 'sam'
del lee.name

for key,val in People.__dict__.items():
    print(key, ' : ', val)

可以看出上面问题的答案:
何地—> 定义成另外一个类的类属性
何时—> 访问、修改和删除上面的类属性时

数据符分两种:
一种是数据描述符: 至少实现了__get__()__set__()
另一种是非数据描述符: 没有实现__set__()

注意事项:

  • 描述符本身应该定义成新式类,被代理的类也应该是新式类
  • 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
  • 要严格遵循该优先级,优先级由高到底分别是
    1. 类属性
    2. 数据描述符
    3. 实例属性
    4. 非数据描述符
    5. 找不到的属性:触发__getattr__()

类属性 > 数据描述符

# Str描述符
class Str:
    def __get__(self, instance, owner):
        print('---> from Str :: __get__')

    def __set__(self, instance, value):
        print('---> from Str :: __set__')

    def __delete__(self, instance):
        print('---> from Str :: __delete__')


class People:
    name = Str()

    def __init__(self, name, age):
        self.name = name
        self.age = age

# 基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
# 那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
People.name # 调用类属性name,本质就是在调用描述符Str,触发了__get__()
People.name = 'lee' # 直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
del People # 同上

数据描述符 > 实例属性

# Str描述符
class Str:
    def __get__(self, instance, owner):
        print('---> from Str :: __get__')

    def __set__(self, instance, value):
        print('---> from Str :: __set__')

    def __delete__(self, instance):
        print('---> from Str :: __delete__')


class People:
    name = Str()

    def __init__(self, name, age):
        self.name = name
        self.age = age


p = People('lee', 24)
p.name
# 如果描述符是一个数据描述符(即有__get__又有__set__),那么p.name的调用与赋值都是触发描述符的操作,于p本身无关了,相当于覆盖了实例的属性
p.name = 'sam'
print(p.__dict__)
# 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
del p.name

实例属性 > 非数据描述符

class Str:
    def __get__(self, instance, owner):
        print('---> from Str :: __get__')

    # def __set__(self, instance, value):
    #     print('---> from Str :: __set__')

    def __delete__(self, instance):
        print('---> from Str :: __delete__')


class People:
    name = Str()

    def __init__(self, age):
        self.age = age


p = People(24)
print(p.name)
# p.name = 'sam'  # 这句会报错,想想为什么
# 因为实例p没有属性name,而类中有此描述符,所以不会新增属性,而是使用查询到的描述符对象,而此描述符为定义__set__()方法,故报错
del p.name
print(p.__dict__)
class Foo:
    def __get__(self, instance, owner):
        print('get')


class Room:
    name = Foo()

    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

# name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
# 对实例的属性操作,触发的都是实例自己的
r = Room('bedroom', 8, 10)
r.name
r.name = 'kitchen'

非数据描述符 > 找不到

class Bar:
    def __get__(self, instance, owner):
        print('get')


class Foo:
    bar = Bar()

    def __getattr__(self, item):
        print('property %s is not found'%item)


f = Foo()
f.age 
f.bar

描述符的使用
众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能。

小试牛刀.py

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

    def __get__(self, instance, owner):
        print('---> get', instance, owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('---> set', instance, value)
        instance.__dict__[self.name] = value
# 更改实例的底层属性字典,修改属性
    def __delete__(self, instance):
        print('---> delete', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name')

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p = People('lee', 24, 8000.0)

print(p.__dict__)
print(p.name) # 触发get

print(p.__dict__)
p.name = 'sam'  # 触发set
print(p.__dict__)

print(p.__dict__)
del p.name  # 触发delete
print(p.__dict__)

拔刀相助.py

# 2
class Str:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print('---> get', instance, owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('---> set', instance, value)
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('---> delete', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name')

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


# 上面的代码,如果用类名去调用name属性会报错
# People.name  # Error,错误的根源在于类去操作属性时,会把None传给instance

# Solution 解决方法,修改__get__方法
class Str:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print('---> get', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('---> set', instance, value)
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('---> delete', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name')

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


print(People.name)  # 已解决~

狭路相逢.py

# 数据描述符实现类型检测
class Str:
    def __init__(self, name, exp_type):
        self.name = name
        self.exp_type = exp_type

    def __get__(self, instance, owner):
        print('---> get', instance, owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('---> set', instance, value)
        if not isinstance(value, self.exp_type):
            raise TypeError('Type %s is expected while given is %s '%(self.exp_type, type(value)))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('---> delete', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name', str)

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p = People('lee', 24, 8000.0)
p2 = People(1993, 23, 6000.0)

大刀阔斧.py

class Typed:
    def __init__(self, name, exp_type):
        self.name = name
        self.exp_type = exp_type

    def __get__(self, instance, owner):
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, self.exp_type):
            raise TypeError('Type %s is expected while given is %s '%(self.exp_type, type(value)))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        instance.__dict__.pop(self.name)


class People:
    name = Typed('name', str)
    age = Typed('age', int)
    salary = Typed('salary', float)

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p1 = People(1993, 24, 6666.6)
p2 = People('lee', '24', 8000.0)
p3 = People('lee', 24, '8000.0')

大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,如果仍然采用在定义一堆类属性的方式去实现,这样写太低端了,我们来学个新技能:独孤九剑
类装饰器无参版:

# class decorator
def decorator(cls):
    print("I'm class decrator")
    return cls

@decorator  # 无参
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p = People('lee', 24, 8000.0)  # p = decorator(People('lee', 24, 8000.0))

类装饰器有参版:

def type_assert(**kwargs):
    def decorator(cls):
        print("I'm class decrator")
        return cls
    return decorator


@type_assert(name=str, age=int, salary=float)  # 有参:1.运行typeassert(...)返回结果是decorator,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p = People('lee', 24, 8000.0)

终极大招.py

class Typed:
    def __init__(self, name, exp_type):
        self.name = name
        self.exp_type = exp_type

    def __get__(self, instance, owner):
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, self.exp_type):
            raise TypeError('Type %s is expected while given is %s '%(self.exp_type, type(value)))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        instance.__dict__.pop(self.name)


def type_assert(**kwargs):
    def decorator(cls):
        for name, exp_type in kwargs.items():
            setattr(cls, name, Typed(name, exp_type))
        return cls
    return decorator


@type_assert(name=str, age=int, salary=float)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


print(People.__dict__)
p = People('lee', '24', 8000.0)

描述符总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性。
描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件。

利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)。
自己实现property:

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self.func(instance)


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # 相当于 area = Lazyproperty(area),巧妙地定义了一个类属性,即描述符
    def area(self):
        return self.width*self.length


r = Room('bedroom', 8, 10)
print(r.area)

实现延时计算功能:

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            # 每计算一次就将结果缓存到实例的属性字典中,实例再次调用时,将直接从属性字典中取值(因为优先级上实例属性大于非数据描述符)
            setattr(instance, self.func.__name__, value)  
            return value


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty
    def area(self):
        return self.width*self.length


r = Room('bedroom', 8, 10)
print(r.area)
print(r.__dict__)

稍微不慎就会出bug,想想下面的代码怎么不能缓存了?

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        print('get')
        if instance is None:
            return self
        else:
            value = self.func(instance)
            # setattr(instance, self.func.__name__, value)
            instance.__dict__[self.func.__name__] = value
            return value

    def __set__(self, instance, value):
        print('set')

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # area = Lazyproperty(area)
    def area(self):
        return self.width*self.length


r = Room('bedroom', 8, 10)
print(Room.__dict__)
print(r.area)  
print(r.area)  # 这时已不能实现懒惰求值了
print(r.__dict__)  # 虽然已经缓存到实例的属性字典中,但由于描述符为属性描述符,优先级高于实例属性,故r.area首先返回描述符,触发__get__方法再次计算结果并返回

利用描述符原理完成一个自定制@classmethod

class ClassMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        def feedback():
            print('do some thing')
            return self.func(owner)
        return feedback


class People:
    name = 'lee'

    @ClassMethod
    def say_hi(cls):
        print('Hi, %s'%cls.name)


People.say_hi()
p = People()
p.say_hi()

# 若类方法有参数呢?  
# 莫慌↓

class ClassMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        def feedback(*args, **kwargs):
            print('do some thing')
            return self.func(owner, *args, **kwargs)
        return feedback


class People:
    name = 'lee'

    @ClassMethod
    def say_hi(cls, msg):
        print('Hi, %s. %s' % (cls.name, msg))


People.say_hi("It's very nice of you!")
p = People()
p.say_hi("It's very nice of you!")

利用描述符原理完成一个自定制的@staticmethod

class StaticMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        def feedback(*args, **kwargs):
            print('do some thing')
            return self.func(*args, **kwargs)
        return feedback


class People:
    @StaticMethod   # say_hi = StaticMethod(say_hi)
    def say_hi(x, y, z):
        print('--->', x, y, z)


People.say_hi(1, 2, 3)
p = People()
p.say_hi(4, 5, 6)

再看property

一个静态属性property本质就是实现了getter,setter,deleter三种方法

class Foo:
    x = 2
    y = 3

    @property
    def bar(self):
        print('---> get')
        return self.x*self.y

    @bar.setter
    def bar(self, value):
        print('---> set')

    @bar.deleter
    def bar(self):
        print('---> delete')


# 只有在属性bar定义property之后才能定义setter和deleter
f = Foo()
f.bar
f.bar = 'bar'
del f.bar

用法2:

class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
# 这种方法不直观

property的应用:

class Goods:
    def __init__(self):
        self.original_price = 100
        self.discount = 0.8

    @property
    def price(self):
        return self.original_price*self.discount

    @price.setter
    def price(self, new_original_price):
        self.original_price = new_original_price

    @price.deleter
    def price(self):
        del self.original_price


toy = Goods()
print(toy.price)
toy.price = 200
print(toy.price)
del toy.price

property实现类型检测:

#实现类型检测功能

#第一关:
class People:
    def __init__(self,name):
        self.name=name

    @property
    def name(self):
        return self.name

# p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常


#第二关:修订版

class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__)

p1.name='egon'
print(p1.__dict__)

del p1.name
print(p1.__dict__)


#第三关:加上类型检查
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            raise TypeError('必须是字符串类型')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
p1.name=1

__setitem__,__getitem__和__delitem__

这三个方法为实例提供了类字典操作,看下面的代码:

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

    def __getitem__(self, item):
        return self.__dict__[item]

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

    def __delitem__(self, key):
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('del obj.key, I run')
        self.__dict__.pop(item)


f = Foo('sam')
print(f['name'])
f['age'] = 18
print(f['age'])
del f['age']
del f.name
f['name'] = 'lee'
print(f.__dict__)

__str__、__repr__和__format__

__str__和__repr__改变对象的字符串显示,当打印对象时可显示定制的内容;__format__提供自定制格式化字符串操作。

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

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

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

    def __format__(self, format_spec):
        if not format_spec:
            format_spec = '-'
        fmt = '{obj.name} %s {obj.addr} %s {obj.type}' %(format_spec, format_spec)
        return fmt.format(obj=self)


s = School("Xauat", "xi'an", "public")
print("from repr: ", repr(s))
print("from str: ", str(s))
print(s)

print(format(s, ':'))
# str函数或者print函数--->obj.__str__()
# repr或者交互式解释器--->obj.__repr__()
# 如果__str__没有被定义,那么就会使用__repr__来代替输出
# 注意:这俩方法的返回值必须是字符串,否则抛出异常
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))
print('{:mdy}'.format(d1))

__slots__

了解:

  1. __slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
  2. 引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
  3. 为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
  4. 注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
  5. 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷, 更多的是用来作为一个内存优化工具。

__slots__用法:

class Foo:
    __slots__ = 'x'


class Bar:
    __slots__ = ['x', 'y']


b = Bar()
b.x =1
b.y =2

f = Foo()
f.x = 1
f.y = 2  # Error
# f与b都没有属性字典__dict__了,统一归__slots__管,节省内存

__next__和__iter__实现迭代器协议

Intro:

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

    def __iter__(self):
        return self

    def __next__(self):
        self.x += 1
        return self.x


f = Foo(1)
for i in f:
    if i > 30000:
        break
    print(i)
class Foo:
    def __init__(self, start, stop):
        self.num = start
        self.stop = stop

    def __iter__(self):
        return self

    def __next__(self):
        if self.num >= self.stop:
            raise StopIteration
        n = self.num
        self.num += 1
        return n


f = Foo(2,10)
from collections import Iterable, Iterator
print(isinstance(f, Iterator))
print(isinstance(f, Iterable))

for i in f:
    print(i)

模拟range(),并加上步长:

class Range:
    step = 1
    num = 0

    def __init__(self, *args):
        if len(args) == 1:
            self.end = args[0]
        if len(args) == 2:
            self.num = args[0]
            self.end = args[1]
        else:
            self.num = args[0]
            self.end = args[1]
            self.step = args[2]

    def __iter__(self):
        return self

    def __next__(self):
        if self.num >= self.end:
            raise StopIteration
        tmp = self.num
        self.num += self.step
        return tmp


for i in Range(0, 10, 2):
    print(i)

斐波那契数列:

class Fib:
    def __init__(self):
        self._a = 0
        self._b = 1

    def __iter__(self):
        return self

    def __next__(self):
        tmp = self._a
        self._a, self._b = self._b, self._a + self._b
        return tmp


f = Fib()
for i in f:
    if i > 200:
        break
    print(i)

__doc__、__module__和__class__

class Foo:
    '我是描述信息'
    pass

print(Foo.__doc__)
# 但该属性无法继承给子类
__module__和__class__:
分别表示当前操作的对象在那个模块、当前操作的对象的类是什么

del

析构方法,当对象在内存中被释放时,自动触发执行。

注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果: 执行我啦, 其实不手动删除,解释器也会自动回收
------->

典型的应用场景:

创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中。当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源。这与文件处理是一个道理:

f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
del f #只回收用户空间的f,操作系统的文件还处于打开状态

#所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
f=open('a.txt')
# 读写...
f.close()
# 很多情况下大家都容易忽略f.close,这就用到了with上下文管理

__enter__和__exit__

在操作文件对象时我们可以这么写:

with open('a.txt') as f:
    # '代码块'

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter____exit__方法。
上下文管理协议:

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    # print(f,f.name)

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行。

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行

但如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行:

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True

with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行

模拟open():

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

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

    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('a.txt', 'w') as f:
    print(f)
    f.write('Hello')
    f.ksksk  # 抛出异常,交给__exit__处理
  • 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预;
  • 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处。

__call__

对象后面加括号,触发执行,相当于C++中的函数对象。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):

        print('__call__')

obj = Foo() # 执行 __init__
obj()       # 执行 __call__

元类 metaclass

猜你喜欢

转载自blog.csdn.net/u010525694/article/details/80226216
今日推荐