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

Posted Jame-mei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

    以上是关于Python基础-week06 面向对象编程进阶的主要内容,如果未能解决你的问题,请参考以下文章

python - 面向对象编程基础知识 (进阶)

进阶学Python:Python面向对象基础!

廖雪峰老师——Python进阶( 面向对象编程基础 )

python基础-第七篇-7.2面向对象(进阶篇)

python基础-面向对象进阶

Python面向对象编程进阶