• python 全栈开发,Day23(复习,isinstance和issubclass,反射)


    一、复习

    class A:
        def func1(self):pass
        def func2(self): pass
        def func3(self): pass
    a = A()
    b = A()
    print(A.func1)
    print(a.func1)
    print(b.func1)

    执行输出:

    <function A.func1 at 0x00000286A24EAA60>
    <bound method A.func1 of <__main__.A object at 0x00000286A2381048>>
    <bound method A.func1 of <__main__.A object at 0x00000286A2381358>>

    bound 表示绑定的意思。当类A实例化时,会将里面的方法与做类做绑定。

    class A:
        def func1(self):pass  # 对象与方法之间的绑定
        @classmethod
        def func2(self): pass  # 类与方法之间的绑定
        @classmethod
        def func3(self): pass
    a = A()
    b = A()
    #普通方法 对象和类绑定的过程
    print(A.func2)
    print(a.func2)  # 对象能找到类 类里面绑着方法
    print(b.func2)

    执行输出:

    <bound method A.func2 of <class '__main__.A'>>
    <bound method A.func2 of <class '__main__.A'>>
    <bound method A.func2 of <class '__main__.A'>>

    类方法 由于不适用对象内存空间中的属性,所以不会将对象和方法绑在一起
    而是将类和方法绑在一起

    class A:
        def func1(self):pass  # 对象与方法之间的绑定
        @classmethod
        def func2(self): pass  # 类与方法之间的绑定
        @staticmethod
        def func3(self): pass  # 静态方法
    a = A()
    b = A()
    print(A.func3)
    print(a.func3)
    print(b.func3)

    执行输出:

    <function A.func3 at 0x0000027EB99EA9D8>
    <function A.func3 at 0x0000027EB99EA9D8>
    <function A.func3 at 0x0000027EB99EA9D8>

    静态方法 不是绑定方法 没有和对象或者类发生任何绑定关系

    python处处皆对象

    'a' 是str的对象

    [1,2,3] 是list的对象

    派生

    class Foo:
        def __init__(self,name):
            self.name = name
            self.age = 18
     
    class Son(Foo):pass
    son = Son('egon')
    print(son.__dict__)

    执行输出:

    {'name': 'egon', 'age': 18}

    这里面没有派生属性(子类有,父类没有的属性)

    增加一个派生属性

    class Foo:
        def __init__(self,name):
            self.name = name
            self.age = 18
     
    class Son(Foo):pass
    son = Son('egon')
    son.money = 500  # 在类外部添加一个属性
    print(son.__dict__)

    执行输出:

    {'money': 500, 'name': 'egon', 'age': 18}

    第二种方式,添加派生属性

    class Foo:
        def __init__(self,name):
            self.name = name
            self.age = 18
     
    class Son(Foo):
        def __init__(self,name,money):
            Foo.__init__(self,name)
            self.money = money  # 初始化添加属性
     
    son = Son('egon',500)
    print(son.__dict__)

    执行输出:

    {'age': 18, 'money': 500, 'name': 'egon'}

    age重新赋值

    class Foo:
        def __init__(self,name):
            self.name = name
            self.age = 18
     
    class Son(Foo):
        def __init__(self,name,money):
            Foo.__init__(self,name)
            self.age = 20  # 重新赋值
            self.money = money  # 初始化添加属性
     
    son = Son('egon',500)
    print(son.__dict__)

    执行输出:

    {'name': 'egon', 'money': 500, 'age': 20}

    在抽象类里面,当一个类指定ABCMeta,就不能实例化了

    面向对象总结,请参考图

    https://www.processon.com/view/link/5ad6a594e4b0f5fa24e09235

    二、isinstance和issubclass

    isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
    isinstance() 与 type() 区别:
      type() 不会认为子类是一种父类类型,不考虑继承关系。
      isinstance() 会认为子类是一种父类类型,考虑继承关系。
    如果要判断两个类型是否相同推荐使用 isinstance()。

    a = 2
    print(isinstance (a,int))  # 判断是否为整形

    执行输出: True

    判断继承关系

    class A:pass
    class B:pass
    class A(B):pass
    a = A()
    print(isinstance(a,A))
    print(isinstance(a,B))  # 能够检测到继承关系
    print(type(a) is A)
    print(type(a) is B)  # type 只单纯的判断类型

    执行输出:

    True
    True
    True
    False

    isinstance判断一个对象和一个类有没有血缘关系

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

    issubclass 检测子类,父类关系

    class C:pass
    class D(C):pass
    print(issubclass(C,D))
    print(issubclass(D,C))

    执行输出:

    False
    True

    issubclass(子类名,父类名) 如果返回True,说明有继承关系

    多继承

    class B:pass
    class C(B):pass
    class D(C):pass
    print(issubclass(C,D))
    print(issubclass(D,C))
    print(issubclass(B,C))
    print(issubclass(C,B))
    print(issubclass(D,B))

    执行输出:

    False
    True
    False
    True
    True

    这2个方法,不常用

    三、反射

    反射非常重要,如果一个知识点,重要程度是5颗星的话,那么反射是6星重点掌握!!!!

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

    2. python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
    反射就是通过字符串的形式,导入模块;通过字符串的形式,去模块寻找指定函数,并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!

    先来介绍四个内置函数:

    ✴✴✴✴✴✴hasattr 判断值是否存在
    ✴✴✴✴✴✴getattr 获取值
    setattr 修改和新建
    delattr 删除一个属性

    参数形式:

    hasattr(obj, attr): 
    这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。
    getattr(obj, attr): 
    调用这个方法将返回obj中名为attr值的属性的值,例如如果attr为'bar',则返回obj.bar。
    setattr(obj, attr, val): 
    调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为'bar',则相当于obj.bar = val
    delattr(obj, name)
    参数是由一个对象和一个字符串组成的。string参数必须是对象属性名之一。该函数删除该obj的一个由string指定的属性。delattr(x, 'foobar')=del x.foobar

    先来讲eval

    role = 123
    print(eval('role'))

    执行输出: 123

    class A:
        role = 'Person'
    print(A.__dict__['role'])

    执行输出:

    Person

    根据输入内容,测试获取的类的属性

    class A:
        role = 'Person'
    ret = input('>>>')
    print(A.__dict__[ret])

    执行输出:

    >>>role
    Person

    那么问题来,如果获取一个函数呢?

    def func():pass
    ret = input('>>>')
    eval(ret)

    如果植入一段代码,删除根目录,那么linux系统就完了。。。

    import os;os.remove('/')

    所以eval不能随便用,这样写没有安全意识
    input的内容,没法估量,有可能是病毒或者恶意代码

    hasattr和getattr的组合使用

    class A:
        role = 'Person'
     
    if hasattr(A,'role'):  # 判断role属性在A中是否存在
        print(getattr(A,'role'))  # 获取role属性的值

    执行输出:

    Person

    getattr 使用字符串数据类型的变量名 访问一个命名空间中的名字

    前提是,必须指定类的命名空间
    比如A,'role'

    如果getattr获取不到值,直接报错,所以一般和hasattr配合使用

    类中的方法也是可以的获取的

    从A的命名空间里找一个方法 ,找到的是这个方法的内存地址

    class A:
        role = 'Person'
        def func(self):
            print('*'*self)
     
    if hasattr(A,'func'):  # 判断func方法在A中是否存在
        print(getattr(A,'func'))  # 获取func方法的值

    执行输出:

    <function A.func at 0x000001E95F8FAA60>

    class A:
        role = 'Person'
        def func(self):
            print('*'*self)
     
    if hasattr(A,'func'):  # 判断func方法在A中是否存在
        print(getattr(A,'func'))  # 获取func方法的值
        #下面三行代码,效果一样
        f = getattr(A,'func');f(1)
        getattr(A,'func')(1)
        #上面2行,是用的字符串方法
        A.func(1)

    执行输出:

    <function A.func at 0x00000217CBC0AA60>
    *
    *
    *

    反射
    正常情况下如果可以拿到这个变量 那么如有有这个变量的字符串形式 就是用反射可以获取到这个值
    使用字符串数据类型的变量名 访问一个命名空间中的名字
    找一个属性 ,直接就可以找到这个属性的值
    找一个方法 ,找到的是这个方法的内存地址
    hasattr() 判断一个命名空间中有没有这个名字
    getattr() 从命名空间中获取这个名字对应的值

    类中的反射
      类可以获取类中的属性和方法

    class A:
        role = 'Person'
        def func(self):
            print('*'*self)
     
    ret = input('>>>')
    if hasattr(A,ret):
        print(getattr(A,ret))

    执行输出:

    >>>func
    <function A.func at 0x00000206EFDFAA60>

    将点改成逗号,将右边的属性名/方法名,换成字符串,比如'role'
    整体就是A,'role'

    再通过getattr就可以获取了。

    class A:
        role = 'Person'
        def func(self):
            print('*'*self)
     
    ret = input('>>>')  # 假设输入的是func
    if hasattr(A,ret):
        func = getattr(A,ret)  # 此时func就是A类的func方法
        func(1)

    执行输出:

    >>>func
    *

    看这一句代码:

    func = getattr(A,ret)

     等式计算右边的,getattr获取的值为A类func方法的内存地址。那么将值赋予给func之后,

    外部的func和类内部的func名字是同名的,此时左边的fun和A类的方法名重名。但是不冲突,为什呢?因为一个在类外部有,一个在类的外表不

    加打印

    class A:
        role = 'Person'
        def func(self):
            print('*'*self)
     
    ret = input('>>>')
    if hasattr(A,ret):
        func = getattr(A,ret)
        print(func)
        func(1)
        print(func)

    查看内存地址:

    >>>func
    <function A.func at 0x000001E9CF8CAA60>
    *
    <function A.func at 0x000001E9CF8CAA60>

    内存地址是一样的

    小练习,使用反射的知识,用对象调用func

    class A:
        role = 'Person'
        def func(self):
            print('*'*10)
     
    a = A()

    答案:

    class A:
        role = 'Person'
        def func(self):
            print('*'*10)
     
    a = A()
    print(a.func)  # 获取a对象的func方法的内存地址
    if hasattr(a,'func'):
        getattr(a,'func')() # 执行func方法

    执行输出:

    <bound method A.func of <__main__.A object at 0x000002A0AD61BB00>>
    **********

    讲一个QQ的例子:

    qq客户端登录的时候,输入用户名和密码,将数据通过网络传输给腾讯服务器

    数据经过编码的,二进制数据。腾讯服务器接收到数据之后,解码得到字符串

    通过判断方法,来执行相应动作。

    假设有500种功能呢?写500层if判断?太low了

    有没有更好的办法呢?用反射

    在A类里面,定义好所有的方法

    用一个if判断,就可以执行对应的方法了。

    class A:
        role = 'Person'
        def __init__(self):
            self.money = 500
        def func(self):
            print('*'*10)
    a = A()
    print(a.func)
    if hasattr(a,'func'):
        getattr(a,'func')()
    'money' #使用字符串
    if hasattr(a,'money'):
        print(getattr(a,'money'))

    执行输出:

    <bound method A.func of <__main__.A object at 0x000001FC2D34FF28>>
    **********
    500

    类使用类命名空间中的名字
    对象使用对象能用的方法和属性
    模块使用模块中的名字
    import os ; getattr(os,'rename')('user','user_info')
    从自己所在的模块中使用自己名字

    import time  # 一个py文件就是一个模块
    time.time()
    if hasattr(time,'time'):
        print(time.time)
        print(getattr(time,'time')())

    执行输出:

    <built-in function time>
    1524044098.9708395

    新建文件teacher.py 内容如下:

    class Teacher:
        todo_list = [
     
        ]
        def __init__(self,name):
            self.name = name

    导入模块teacher

    import teacher
    t = 'Teracher'
    alex = teacher.t('alex').name
    print(alex)

    执行输出:alex

    import teacher
    t = 'Teacher'
    Terach_class = getattr(teacher,t)
    print(Terach_class)
    print(teacher.Teacher) 

    执行输出:

    <class 'teacher.Teacher'>
    <class 'teacher.Teacher'>

    假设有一个文件名userinfo,需要重命名

    第一种:常规办法

    import os
    os.rename('userinfo','user')

    第二种:使用反射

    import os<br>#os.rename('userinfo','user')
    getattr(os,'rename')('user','user_info')
    
    a = 1
    b = 2
    def login():
        print('执行login功能')
    def register():
        print('执行register功能')
    func = input('>>>')

    怎么反射自己的模块名?

    使用sys模块,和python解释器相关的内容都在sys里

    import sys
    print(sys.modules['__main__'])  # 查看当前文件路径,包含文件名

    完整代码如下:

    def login():
        print('执行login功能')
    def register():
        print('执行register功能')
    import sys
    #print(sys.modules['__main__'])  # 找到自己所在的模块(py文件)
    func = input('>>>')
    getattr(sys.modules['__main__'],'login')()

    执行输出:

    >>>login
    执行login功能

    总结:

    类使用类命名空间中的名字
      getattr(类名,'名字')
    对象使用对象能用的方法和属性
      getattr(对象名,'名字')
    模块使用模块中的名字
      导入模块
      getattr(模块名,'名字')
      import os ; getattr(os,'rename')('user','user_info')
    从自己所在的模块中使用自己名字
      import sys
      getattr(sys.modules['__main__'],名字)

    getattr一定要和hasattr配合使用

    setattr

    class A:
        def __init__(self,name):
            self.name = name
        def wahaha(self):
            print('wahaha')
    a = A('alex')
    a.name
    a.wahaha()

    可以给a实例,绑定一个属性age

    class A:
        def __init__(self,name):
            self.name = name
        def wahaha(self):
            print('wahaha')
    a = A('alex')
    print(a.__dict__)
    setattr(a,'age',18)  # 给a对象新增一个属性
    print(a.__dict__)

    执行输出:

    {'name': 'alex'}
    {'name': 'alex', 'age': 18}

    再增加一个属性

    class A:
        def __init__(self,name):
            self.name = name
        def wahaha(self):
            print('wahaha')
    a = A('alex')
    print(a.__dict__)
    setattr(a,'age',18)  # 给a增加一个属性
    print(a.__dict__)
    setattr(a,'name','egon')  # 修改name属性
    print(a.__dict__)

    执行输出:

    {'name': 'alex'}
    {'name': 'alex', 'age': 18}
    {'name': 'egon', 'age': 18}

    删除一个属性

    class A:
        def __init__(self,name):
            self.name = name
        def wahaha(self):
            print('wahaha')
    a = A('alex')
    delattr(a,'name')
    print(a.__dict__)

    执行输出:{}

    很少有删除一个方法的
    上面都是倒腾属性的,
    下面说增删改方法,非常非常不常用

    class A:
        def __init__(self,name):
            self.name = name
        def wahaha(self):
            print('wahaha')
     
    def qqxing(self):
        print('qqxing')
    a = A('alex')
    setattr(a,'qqxing',qqxing)  # 增加一个属性
    print(a.__dict__)<br>a.qqxing

    执行输出:

    {'name': 'alex', 'qqxing': <function qqxing at 0x00000216743F7F28>}

    不能set给对象添加一个方法
    否则a.qqxing()就报错
    这样才行

    class A:
        def __init__(self,name):
            self.name = name
        def wahaha(self):
            print('wahaha')
     
    def qqxing(self):
        print('qqxing')
    a = A('alex')
    setattr(a,'qqxing',qqxing)  # 增加一个属性
    # print(a.__dict__)
    a.qqxing(a)

    执行输出:qqxing

    面试也不会问 set方法
    方法可以绑定给类
    但是不能绑定给对象

    重点记住:

    getattr

    hasattr

    getatter和hasattr一般是成对出现,先判断,使用hasattr,再getatter

    实例化一个list

    li = list([1,2,3,4,5])
    print(li)

    li是对象,list是类

    查看长度,使用len方法

    li = list([1,2,3,4,5])
    print(li)
    print(len(li))

    执行输出:

    [1, 2, 3, 4, 5]
    5

    那么类,可以使用len方法吗?

    class A:
        def __init__(self,name,age,sex,cls):
            self.name = name
            self.age = age
            self.sex = sex
            self.cls = cls
    a = A('alex',81,'不详',2)
    print(len(a))

    执行输出:

    TypeError: object of type 'A' has no len()

    那么非要计算长度呢?

    有一个内置函数__len__和内置方法len()是唯一对应的关系

    当一个对象拥有__len__方法时,就可以使用len()方法时。

    class A:
        def __init__(self,name,age,sex,cls):
            self.name = name
            self.age = age
            self.sex = sex
            self.cls = cls
     
        def __len__(self):  # 添加__len__方法
            return 5
         
    a = A('alex',81,'不详',2)
    print(len(a))

    执行输出:

    5

    多实例化几次呢?

    class A:
        def __init__(self,name,age,sex,cls):
            self.name = name
            self.age = age
            self.sex = sex
            self.cls = cls
     
        def __len__(self):  # 添加__len__方法
            return 5
     
    a1 = A('alex',81,'不详',2)
    a2 = A('egon',20,'不详',3)
    a3 = A('yuan',21,'不详',4)
    print(len(a1))
    print(len(a2))
    print(len(a3))

    执行输出:

    5

    5

    5

    现在想要__len__有实际意义
    统计属性的个数

    class A:
        def __init__(self,name,age,sex,cls):
            self.name = name
            self.age = age
            self.sex = sex
            self.cls = cls
     
        def __len__(self):  # 添加__len__方法
            return len(self.__dict__)  # 通过__dict__方法,可以查看对象的属性
     
    a1 = A('alex',81,'不详',2)
    a1.hobby = '烫头'
    a2 = A('egon',20,'不详',3)
    a3 = A('yuan',21,'不详',4)
    print(len(a1))
    print(len(a2))
    print(len(a3))

    执行输出:

    5
    4
    4

    内置的东西,都和内置的方法有着千丝万缕的联系

    可哈希和不可哈希
    字典的寻址速度是最快的
    它用的是哈希算法

    在一次python程序运行的过程中,对同一个字符串进行运算之后,会得到一个数字
    字典的key不能一样

    一个对象,能不能hash

    靠一个内置方法__hash__决定的

    class A:
        def __init__(self,name,age,sex,cls):
            self.name = name
            self.age = age
            self.sex = sex
            self.cls = cls
     
    a1 = A('alex',81,'不详',2)
    print(hash(a1))

    执行输出:

    141908978608

    a1这个对象,为什么可以被hash呢?

    先看一下object源码,它包含了__hash__方法

    def __hash__(self, *args, **kwargs): # real signature unknown
        """ Return hash(self). """
        pass

    由于python3所有类都继承了object类,所以a1对象之所以可以hash,是靠object类来完成的。

    可以自己定义个__hash__方法

    class A:
        def __init__(self,name,age,sex,cls):
            self.name = name
            self.age = age
            self.sex = sex
            self.cls = cls
     
        def __hash__(self):
            return 0
     
    a1 = A('alex',81,'不详',2)
    print(hash(a1))

    执行输出: 0

    明日默写:

    class A:
        role = 'Person'
        def __init__(self):
            self.money = 500
        def func(self):
            print('*'*10)
     
    a = A()
    print(a.func)
    getattr(a,'func')()
    print(getattr(a,'money'))

    还有一个

    def login():
        print('执行login功能')
     
    def register():
        print('执行register功能')
     
    import sys  # 和python解释器相关的内容都在sys里
    print(sys.modules['__main__'])
    func = input('>>>')
    if hasattr(sys.modules['__main__'],func):
        getattr(sys.modules['__main__'],func)()
  • 相关阅读:
    android入门教程(十六)之 使用Intent传递数据
    Android入门教程(十八)之ListView (二) (转)
    Android入门教程(十四)之ListView的应用 (转)
    Android入门教程(十七)之GridView(转自http://blog.csdn.net/hellogv/)
    (原创)Android入门教程(十五)之 Activity生命周期及其配置使用
    Android入门教程(十九)之ListView (三) (转)
    Android入门教程(十三)之自定义下拉菜单模式Spinner与setDropDownViewResource的应用(转)
    Android入门教程(十)之Menu功能菜单设计 (转)
    [vp]ARC059
    欧拉路学习笔记
  • 原文地址:https://www.cnblogs.com/tu240302975/p/12688922.html
Copyright © 2020-2023  润新知