Python基础-week06 面向对象编程进阶

一.反射

  1.定义:指的是通过字符串来操作类或者对象的属性

  2.为什么用反射?

    减少冗余代码,提升代码质量。

  3.如何用反射?

    

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


obj=People('jame')


#hasattr
#print('country' in People.__dict__)
print(hasattr(People,'country'))

#getattr
#print(People.__dict__['country'])
#print(getattr(People,'country)) #如果取不到值,会报错。
print(getattr(People,'country',None)) #None如果取不到值不报错,返回None


#setattr
#obj.age=18
#print(obj.__dict__)
setattr(obj,'age',18)
print(obj.__dict__) #{'name': 'jame', 'age': 18}
setattr(People,'x',111)
print(People.__dict__)
#{......, '__doc__': None, 'x': 111}


#delattr
delattr(People,'x')
print(People.__dict__)
#{......, '__doc__': None}
例1:反射涉及的4个内置函数
class Ftp:
    def get(self):
        print('get...')


    def put(self):
        print('put...')


    def auth(self):
        print('auth...')



    def run(self):
        while True:
            cmd=input('Please input:').strip() #cmd='get
            if hasattr(self,cmd):
                method=getattr(self,cmd)
                method()

            else:
                print('You please input error')



obj=Ftp()
obj.run()
例2:反射的简单使用

 

  动态导入模块<了解>:

  

   

import importlib
 
__import__('import_lib.metaclass') #这是解释器自己内部用的
#importlib.import_module('import_lib.metaclass') #与上面这句效果一样,官方建议用这个

 

  

二.一些内置方法 和 内置函数介绍

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

    isinstance(obj,cls)检查是否obj是否是类 cls 的对象。

    issubclass(sub,super) 检查sub类是否是 super类的派生类。    

class Foo(object):
    pass


class bar(Foo):
    pass



obj=Foo()

#1 isinstance判断对象是否属于某类
res1=isinstance(obj,Foo)
print(res1) #True

#2 issubclass 判断bar类是否是 Foo 类的派生类
res2=issubclass(bar,Foo)
print(res2) #true
View Code

 

  2.__setattr__ ,__delattr__ ,__getattr__

   配合反射机制使用,效果还不错. 

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


obj=People('jame')


#hasattr
#print('country' in People.__dict__)
print(hasattr(People,'country'))

#getattr
#print(People.__dict__['country'])
#print(getattr(People,'country123')) #如果取不到值,会报错。AttributeError: type object 'People' has no attribute 'country123'
print(getattr(People,'country123',None)) #None如果取不到值不报错,返回None


#setattr
#obj.age=18
#print(obj.__dict__)
setattr(obj,'age',18)
print(obj.__dict__) #{'name': 'jame', 'age': 18}
setattr(People,'x',111)
print(People.__dict__)
#{......, '__doc__': None, 'x': 111}


#delattr
delattr(People,'x')
print(People.__dict__)
#{......, '__doc__': None}
在类外部使用实例 
class Foo:
    x=1

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


    def __getattr__(self, item):
        print('from getattr:你找的属性不存在.')



    def __setattr__(self, key, value):
        print('from setattr')
        
        

    def __delattr__(self, item):
        print('from delattr')
        self.__dict__.pop(item)



#1 __setattr__ 添加、修改属性会触发它的执行
f1=Foo(10) #from setattr
print(f1.__dict__) #{}
#因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.__dict__['a']=3
f1.__dict__['b']=4
print(f1.__dict__) #{'a': 3, 'b': 4}




#2 __deattr__ 删除属性的时候会触发
#f1.__dict__['a']=10 #我们可以通过修改属性字典,来完成添加、修改属性的操作
del f1.a #from delattr
print(f1.__dict__) #{'b':4}还剩b



# 3 __getattr__ 只有使用点调用属性且属性不存在的时候才会触发
f1.abc #from getattr:你找的属性不存在.
View Code
class Ftp:
    def get(self):
        print('get...')


    def put(self):
        print('put...')


    def auth(self):
        print('auth...')



    def run(self):
        while True:
            cmd=input('Please input:').strip() #cmd='get
            if hasattr(self,cmd):
                method=getattr(self,cmd)
                method()

            else:
                print('You please input error')



obj=Ftp()
obj.run()
配合反射使用实例

 

   3.__getattribute__

    

# @Time    : 2018/8/20 17:19
# @Author  : Jame
# class Foo:
#     def __init__(self,x):
#         self.x=x
#
#     def __getattr__(self, item):
#         print('执行的是__getattr__')
#
#
#
#
# f1=Foo(100)
# print(f1.x)
# f1.xxx #若访问的不存在,则 “执行的是__getattr__”


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

    def __getattribute__(self, item):
        print('不管是否存在都执行的是__getattribute__')


f1=Foo(200)
f1.x
f1.xxx
getattr与getattribute的使用
class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('如果不存在则执行__getattr__')

    def __getattribute__(self, item):
        print('不管是否存在都执行的是__getattribute__')
        raise AttributeError('哈哈 嘿嘿 哟哟')


f1=Foo(200)
f1.x
f1.xxx
getattr和getattribute同时存在实例
#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

 

  4.描述符(__get__ , __set__, __delete__)

    (1).1 描述符是什么?

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

   

#1 定义一个描述符
class Foo:
    def __get__(self, instance, owner):
        print('__get__')

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

    def __delete__(self, instance):
        print('__delete__')
定义一个描述符

 

 

    (2).描述符是干什么的,何时触发描述符中的3个方法呢?

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

      包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法!例如:

      

#2 描述符的使用
class Foo2:
    def __get__(self, instance, owner):
        print('触发 __get__')

    def __set__(self, instance, value):
        print('触发 __set__')

    def __delete__(self, instance):
        print('触发 __delete__')


#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法!
f2=Foo2()
f2.name='jame'
print(f2.name)
del f2.name
描述符的实例进行调用/赋值/删除不会触发

       

      #何地?:定义成另外一个类的类属性

      #何时?:且看下列演示
class Str:
    def __get__(self, instance, owner):
        print('Str 调用')

    def __set__(self, instance, value):
        print('str 设置')

    def __delete__(self, instance):
        print('str 删除')


class Int:
    def __get__(self, instance, owner):
        print('Int 调用')

    def __set__(self, instance, value):
        print('Int 设置')

    def __delete__(self, instance):
        print('Int 删除')


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

    def __init__(self,name,age): #name 被设置为Str类的的代理,age被设置Int类的代理。
        self.name=name
        self.age=age


#何地?:定义成另外一个类的类属性

#何时?:且看下列演示

p1=People('jame',18) #触发Str 设置,Int 设置!


#1 描述符str的使用 调用,设置,删除
#p1.name
#p1.name='tom'
#del p1.name
'''
Str 调用
str 设置
str 删除
'''

#2 描述符int的使用  调用,设置,删除
#p1.age
#p1.age=30
#del p1.age
'''
Int 调用
Int 设置
Int 删除
'''


#3 我们来瞅瞅到底发生了什么?
print(p1.__dict__)
print(People.__dict__)


#补充
print(type(p1) == People) #True,type(p1) 查看p1是哪个类实例化来的。
print(type(p1).__dict__==People.__dict__) #True
触发描述符的场景

 

    (3).描述符分两种

      1).数据描述符:至少实现了__get__()和__set__()      

1 class Foo:
2     def __set__(self, instance, value):
3         print('set')
4     def __get__(self, instance, owner):
5         print('get')

 

      2).非数据描述符:没有实现__set__()

  

class Foo:
2     def __get__(self, instance, owner):
3         print('get')

 

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

      

#描述符str

class Str:
    def __get__(self, instance, owner):
        print('str 调用')


    def __set__(self, instance, value):
        print('Str 设置')


    def __delete__(self, instance):
        print('Str 删除')



class People:
    name=Str()

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



People.name #调用类属性name,本质就是在调用描述符Str,触发了__get__()

People.name='egon' #赋值并没有触发__set__()的设置


del People.name #删除也并没有触发 __del__() 的设置


#结论:描述符对类没有作用-------->傻逼到家的结论
'''

原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()

People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
del People.name #同上

'''
类属性>数据描述符

 

#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')


class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age


p1=People('egon',18) #str设置,触发!

#如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name='egonnnnnn' #str设置,触发!

p1.name #str调用,触发!

print(p1.__dict__) #实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
del p1.name #str删除,触发!

###数据描述符>实例属性
数据描述符>实例描述符

 

class Foo:
    def func(self):
        print('我胡汉三又回来了')


f1=Foo()
f1.func()
#调用类的方法,也可以说是调用非数据描述符
#函数是一个非数据描述符对象(一切皆对象么)


print(dir(Foo.func))

print(hasattr(Foo.func,'__set__')) #False

print(hasattr(Foo.func,'__get__')) #True

print(hasattr(Foo.func,'__del__')) #False
#也有人可能问,描述符不都是类吗,函数怎么算也应该是一个对象,怎么就是描述符了。
#描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
#函数就是有一个非描述符类实例化得到的一个对象
#没错,字符串也是一样



f1.func='这是实例属性啊'

print(f1.func)


del f1.func #删除了非数据
f1.func()   #我胡汉三又回来了
实例属性>非数据描述符

 

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

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




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



#name 是一个数据描述符,因为Name=Foo() 而Foo实现了get 和set方法,因此比实例属性有更高的优先级
#对实例的属性操作,触发的都是描述符
# r1=Room('厕所',1,1) #触发foo set
#
# r1.name            #触发foo get
#
# r1.name='厨房'     #触发 foo set



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


class Room1:
    name=Foo1()

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

#name 是一个非数据描述符,因为name=Foo1() 但是Foo1中没有实现set方法,因而比实例属性的优先级更低
#对实例的属性操作,触发的都是实例自己
r2=Room1('大厦',100,100)
r2.name
r2.name='高塔'
再次验证:实例属性>非数据描述符

 

# @Time    : 2018/8/24 14:56
# @Author  : Jame
class Foo:
    def func(self):
        print('我胡汉三回来了')


    def __getattr__(self, item):
        print('找不到了当然是来找我啦',item)



f1=Foo()


f1.xxxx #找不到了当然是来找我啦 xxxx
非数据描述符>找不到

 

 

    (5).描述符的使用

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

    

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('---del---',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


p1=People('egon',18,3232.3) #Str---get---> <__main__.People object at 0x0000000002866630> egon

#1 调用
print(p1.__dict__) #{'name': 'egon', 'age': 18, 'salary': 3232.3}
p1.name #Str--get--> <__main__.People object at 0x00000000024F6630> <class '__main__.People'>


#2 赋值
print(p1.__dict__) #{'name': 'egon', 'age': 18, 'salary': 3232.3}
p1.name='jame-mei' #---set---> <__main__.People object at 0x00000000024E6668> jame-mei
print(p1.__dict__) #{'name': 'jame-mei', 'age': 18, 'salary': 3232.3}


#3 删除
print(p1.__dict__) #{'name': 'jame-mei', 'age': 18, 'salary': 3232.3}
del p1.name        #---del--- <__main__.People object at 0x0000000002566668>
print(p1.__dict__) #{'age': 18, 'salary': 3232.3}
1 小试菜刀

 

# @Time    : 2018/8/24 15:12
# @Author  : Jame
# class Str:
#     def __init__(self,name):
#         self.name=name
#
#
#     def __get__(self, instance, owner):
#         print('get--->',instance,owner)
#         return instance.__dic__[self.name]
#
#
#     def __set__(self, instance, value):
#         print('set--->',instance,value)
#         instance.__dic__[self.name]=value
#
#     def __delete__(self, instance):
#         print('delete--->',instance)
#         instance.__dic__.pop(self.name)
#
#
# class People:
#     name=Str('name')
#     def __init__(self,name,age,salary):
#         self.name=name
#         self.age=age
#         self.salary=salary

#疑问,如果我用类名去操作属性呢
#People.name  #报错,在于把类去操作属性,会把None传给instance


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.__dic__[self.name]


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

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


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary



People.name    #get---> None <class '__main__.People'>  解决
2 拔吊相助

 

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

    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)
        if not instance(value,self.expected_type):
            raise TypeError('Expected %s'%str(self.expected_type))

        instance.__dict__[self.name]=value

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



class People:
    name=Str('name',str) #新增类型限制str
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary


p1=People(123,18,333.3) #传入的name 因不是字符串类型而抛出异常
#TypeError: 'People' object is not callable
3 磨刀霍霍

 

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

    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)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s'%str(self.expected_type))
        instance.__dict__[self.name]=value


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


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



#p1=People(123,18,33.33) #set---> <__main__.People object at 0x0000000002896780> 123
#TypeError: Expected <class 'str'>

#p2=People('tom','18',33.33) #set---> <__main__.People object at 0x00000000028967F0> tom
#TypeError: Expected <class 'int'>

p3=People('jame',18,33.33)
#set---> <__main__.People object at 0x00000000024A67B8> jame
#set---> <__main__.People object at 0x00000000024A67B8> 18
#set---> <__main__.People object at 0x00000000024A67B8> 33.33
4 大刀阔斧

 

  经过上述几个例子虽然能逐步实现了功能,但是问题是我们的类有很多属性的时候,仍然采用定义一堆属性的方式去实现,比较low,所以需要通过装饰器来实现:

def decorate(cls):
    print('类的装饰器开始运行啦...')
    return cls


@decorate
class People1:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary



p1=People1('jame',18,33.33) #类的装饰器开始运行啦...



def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦----',kwargs)
        return cls

    return decorate


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


p2=People2('jame',18,23.3)
#类的装饰器开始运行啦---- {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
5 无参和有参的类的装饰器

 

   

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


    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)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s'%str(self.expected_type))

        instance.__dict__[self.name]=value

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




def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦---->',kwargs)
        for name,expected_type in kwargs.items():
            setattr(cls,name,Typed(name,expected_type))

        return cls
    return decorate


@typeassert(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__)
p1=People('jame',18,28888.8)
#set---> <__main__.People object at 0x00000000028C6860> jame
#set---> <__main__.People object at 0x00000000028C6860> 18
#set---> <__main__.People object at 0x00000000028C6860> 28888.8
6 终极葵花宝典

 

    (6).描述符总结

      描述符是可以实现大部分Python类特性中的底层魔法,包括@classmethod ,@staticmethod,@property 甚至是__slots__属性的

      描述符是很多高级哭和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件。

 

    (7).用描述符原理完成一个自定制@property,实现延迟计算.

      (本质就是把一个函数属性利用装饰器原理 做成一个描述符:类的类型字典中函数名为key,valued为描述符类产生的 对象)

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


    @property #可以把一个函数当作属性来使用!
    def area(self):
        return self.width*self.length



r1=Room('jame',3,4)
print(r1.area)
1 回顾property

 

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

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性, r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        return self.func(instance) ##此时你应该明白,到底是谁在为你做自动传递self的事情



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



r1=Room('tom',3,4)
print(r1.area)
#这是我们自己定制的静态属性, r1.area实际是要执行r1.area()
#12
2 自己定义一个@property

 

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

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            print('----->')
            value=self.func(instance)
            setattr(instance,self.func.__name__,value)




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



r1=Room('tom',3,4)
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
3 实现延迟计算功能

 

 

 

    (8).用描述符原理完成一个自定制  @classmethod

       

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


    def __get__(self, instance, owner):#类来调用,instance为None,owner为类本身,实例用来调用,instance为实例,owner为类本身
        def feedback():
            print('在这里可以加功能...')
            return self.func(owner)

        return feedback


class People:
    name='jame'
    @ClassMethod #say_hi=ClassMethod(say_hi)
    def say_hi(cls):
        print('你好啊 ,帅哥 %s'%cls.name)


People.say_hi()
#在这里可以加功能...
#你好啊 ,帅哥 jame

p1=People()
p1.say_hi()
#在这里可以加功能...
#你好啊 ,帅哥 jame






#疑问:类方法如果有参数呢?
class ClassMethod2:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner):
        def feedback(*args,**kwargs):
            print('在这里可以加功能...')
            return self.func(owner,*args,**kwargs)

        return feedback


class People2:
    name='tom'
    @ClassMethod2  #say_hi=ClassMethod2(say_hi)
    def say_hi(cls,msg):
        print('你好啊,帅哥 %s %s'%(cls.name,msg))



People2.say_hi('你是那偷心的贼')


#p2=People2()
#p2.say_hi('你是哪偷心的贼')
只定义@classmethod

 

    (9).用描述符原理完成一个自定制 @staticmethod

    

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

    def __get__(self, instance, owner):
        def feedback(*args,**kwargs):
            print('在这里可以加功能...')
            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)

p1=People()
p1.say_hi(4,5,6)
自己做一个@staticmethod

 

 

  5.再看Property 

    一个静态属性property本质就是实现了get,set,delete三种方法

    

 

  6.__setitem__,__getitem,__delitem__

 

  7.__str__,__repr__,__format__

 

  8.__slots__

 

  9.__next__  和 __iter__ 实现迭代器的协议

 

  10.__doc__

 

  11.__module__ 和__class__

 

  12.__del__

 

  13.__enter__ 和 __exit__

 

  14.__call__

 

  15.metaclass

 

 

三.元类

  待更新...

 

 

四.异常处理

  1.什么是异常?

    异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止),在python中,错误触发的异常如下:

    

 

    而错误分成两种:

    

#语法错误示范一
if
#语法错误示范二
def test:
    pass
#语法错误示范三
class Foo
    pass
#语法错误示范四
print(haha
1.语法错误(这种错误,根本过不了python解释器的语法测试,必须在程序执行前就改成!)
#TypeError:int类型不可迭代
for i in 3:
    pass
#ValueError
num=input(">>: ") #输入hello
int(num)

#NameError
aaa

#IndexError
l=['egon','aa']
l[3]

#KeyError
dic={'name':'egon'}
dic['age']

#AttributeError
class Foo:pass
Foo.x

#ZeroDivisionError:无法完成计算
res1=1/0
res2=1+'str'

2.逻辑错误
2.逻辑错误

 

  2.异常的种类?

    在python中不同的异常可以用不同的类型(python 中统一了类与类型,类型即是类) 去标识,一个异常标识就是一种错误.

    

AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
常见异常名称

 

ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

更多异常
更多异常

 

  3.异常处理

    为了保证程序的健壮性与容错性,即在遇到错误时程序不会崩溃,我们需要对异常进行处理,

    如果错误发生的条件是可预知的,我们需要用if进行处理:在错误发生之前进行预防

    

AGE=10
while True:
    age=input('>>: ').strip()
    if age.isdigit(): #只有在age为字符串形式的整数时,下列代码才不会出错,该条件是可预知的
        age=int(age)
        if age == AGE:
            print('you got it')
            break
if 提前预防方式处理

    

    如果错误发生的条件是不可预知的,则需要用到try...except:在错误发生之后进行处理

    

#基本语法为
try:
    被检测的代码块
except 异常类型:
    try中一旦检测到异常,就执行这个位置的逻辑
#举例
try:
    f=open('a.txt')
    g=(line.strip() for line in f)
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
except StopIteration:
    f.close()
try ...except方式处理
#1 异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
s1 = 'hello'
try:
    int(s1)
except IndexError as e: # 未捕获到异常,程序直接报错
    print e


#2 多分支
s1 = 'hello'
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)



#3 万能异常Exception
s1 = 'hello'
try:
    int(s1)
except Exception as e:
    print(e)



#4 多分支异常与万能异常

#4.1 如果你想要的效果是,无论出现什么异常,我们统一丢弃,或者使用同一段代码逻辑去处理他们,那么骚年,大胆的去做吧,只有一个Exception就足够了。
#4.2 如果你想要的效果是,对于不同的异常我们需要定制不同的处理逻辑,那就需要用到多分支了。




#5 也可以在多分支后来一个Exception
s1 = 'hello'
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
except Exception as e:
    print(e)




#6 异常的其他机构
s1 = 'hello'
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
#except Exception as e:
#    print(e)
else:
    print('try内代码块没有异常则执行我')
finally:
    print('无论异常与否,都会执行该模块,通常是进行清理工作')



#7 主动触发异常
try:
    raise TypeError('类型错误')
except Exception as e:
    print(e)

#8 自定义异常
class EgonException(BaseException):
    def __init__(self,msg):
        self.msg=msg
    def __str__(self):
        return self.msg

try:
    raise EgonException('类型错误')
except EgonException as e:
    print(e)

#9 断言:assert 条件
assert 1 == 1  
assert 1 == 2
try-except 多种应用实例

    

    #10 总结try..except

    1:把错误处理和真正的工作分开来
   2:代码更易组织,更清晰,复杂的工作任务更容易实现;
   3:毫无疑问,更安全了,不至于由于一些小的疏忽而使程序意外崩溃了;

 

  4.什么时候用异常处理?  

    Try--except是你附加给你的程序的一种异常处理的逻辑,与你的主要的工作是没有关系的,这种东西加太多会导致代码可读性变差,只有在错误发生的条件无法预知的情况下,才应该加上try ... except!

 

posted @ 2018-08-24 11:14  meijinmeng  阅读(304)  评论(0编辑  收藏  举报