一、复习

  1. class A:
  2. def func1(self):pass
  3. def func2(self): pass
  4. def func3(self): pass
  5. a = A()
  6. b = A()
  7. print(A.func1)
  8. print(a.func1)
  9. print(b.func1)

执行输出:

>

>

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

  1. class A:
  2. def func1(self):pass # 对象与方法之间的绑定
  3. @classmethod
  4. def func2(self): pass # 类与方法之间的绑定
  5. @classmethod
  6. def func3(self): pass
  7. a = A()
  8. b = A()
  9. #普通方法 对象和类绑定的过程
  10. print(A.func2)
  11. print(a.func2) # 对象能找到类 类里面绑着方法
  12. print(b.func2)

执行输出:

>

>

>

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

而是将类和方法绑在一起

  1. class A:
  2. def func1(self):pass # 对象与方法之间的绑定
  3. @classmethod
  4. def func2(self): pass # 类与方法之间的绑定
  5. @staticmethod
  6. def func3(self): pass # 静态方法
  7. a = A()
  8. b = A()
  9. print(A.func3)
  10. print(a.func3)
  11. print(b.func3)

执行输出:

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

python 处处皆对象

‘a’ 是 str 的对象

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

派生

  1. class Foo:
  2. def __init__(self,name):
  3. self.name = name
  4. self.age = 18
  5. class Son(Foo):pass
  6. son = Son('egon')
  7. print(son.__dict__)

执行输出:

{‘name’: ‘egon’, ‘age’: 18}

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

增加一个派生属性

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

执行输出:

{‘money’: 500, ‘name’: ‘egon’, ‘age’: 18}

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

  1. class Foo:
  2. def __init__(self,name):
  3. self.name = name
  4. self.age = 18
  5. class Son(Foo):
  6. def __init__(self,name,money):
  7. Foo.__init__(self,name)
  8. self.money = money # 初始化添加属性
  9. son = Son('egon',500)
  10. print(son.__dict__)

执行输出:

{‘age’: 18, ‘money’: 500, ‘name’: ‘egon’}

age 重新赋值

  1. class Foo:
  2. def __init__(self,name):
  3. self.name = name
  4. self.age = 18
  5. class Son(Foo):
  6. def __init__(self,name,money):
  7. Foo.__init__(self,name)
  8. self.age = 20 # 重新赋值
  9. self.money = money # 初始化添加属性
  10. son = Son('egon',500)
  11. 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()。

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

执行输出:True

判断继承关系

  1. class A:pass
  2. class B:pass
  3. class A(B):pass
  4. a = A()
  5. print(isinstance(a,A))
  6. print(isinstance(a,B)) # 能够检测到继承关系
  7. print(type(a) is A)
  8. print(type(a) is B) # type 只单纯的判断类型

执行输出:

True

True

True

False

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

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

issubclass 检测子类,父类关系

  1. class C:pass
  2. class D(C):pass
  3. print(issubclass(C,D))
  4. print(issubclass(D,C))

执行输出:

False

True

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

多继承

  1. class B:pass
  2. class C(B):pass
  3. class D(C):pass
  4. print(issubclass(C,D))
  5. print(issubclass(D,C))
  6. print(issubclass(B,C))
  7. print(issubclass(C,B))
  8. print(issubclass(D,B))

执行输出:

False

True

False

True

True

这 2 个方法,不常用

三、反射

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

1. 什么是反射

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

  1. 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

  1. role = 123
  2. print(eval('role'))

执行输出:123

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

执行输出:

Person

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

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

执行输出:

role

Person

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

  1. def func():pass
  2. ret = input('>>>')
  3. eval(ret)

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

  1. import os;os.remove('/')

所以 eval 不能随便用,这样写没有安全意识

input 的内容,没法估量,有可能是病毒或者恶意代码

hasattr 和 getattr 的组合使用

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

执行输出:

Person

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

前提是,必须指定类的命名空间

比如 A,’role’

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

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

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

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

执行输出:

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

执行输出:

*

*

*

反射

正常情况下如果可以拿到这个变量 那么如有有这个变量的字符串形式 就是用反射可以获取到这个值

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

找一个属性 ,直接就可以找到这个属性的值

找一个方法 ,找到的是这个方法的内存地址

hasattr() 判断一个命名空间中有没有这个名字

getattr() 从命名空间中获取这个名字对应的值

类中的反射

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

  1. class A:
  2. role = 'Person'
  3. def func(self):
  4. print('*'*self)
  5. ret = input('>>>')
  6. if hasattr(A,ret):
  7. print(getattr(A,ret))

执行输出:

func

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597655199199-b075de3f-4d16-462d-a29a-872bbf102bba.png)

将点改成逗号,将右边的属性名/方法名,换成字符串,比如’role’

整体就是 A,’role’

再通过 getattr 就可以获取了。

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

执行输出:

func

*

看这一句代码:

  1. func = getattr(A,ret)

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

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

加打印

  1. class A:
  2. role = 'Person'
  3. def func(self):
  4. print('*'*self)
  5. ret = input('>>>')
  6. if hasattr(A,ret):
  7. func = getattr(A,ret)
  8. print(func)
  9. func(1)
  10. print(func)

查看内存地址:

  1. >>>func
  2. <function A.func at 0x000001E9CF8CAA60>
  3. *
  4. <function A.func at 0x000001E9CF8CAA60>

内存地址是一样的

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

  1. class A:
  2. role = 'Person'
  3. def func(self):
  4. print('*'*10)
  5. a = A()

答案:

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

执行输出:

>


讲一个 QQ 的例子:

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

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

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

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

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597655199309-f88267ad-96a4-4343-8085-c971b33aa5ab.png)

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

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

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

  1. class A:
  2. role = 'Person'
  3. def __init__(self):
  4. self.money = 500
  5. def func(self):
  6. print('*'*10)
  7. a = A()
  8. print(a.func)
  9. if hasattr(a,'func'):
  10. getattr(a,'func')()
  11. 'money' #使用字符串
  12. if hasattr(a,'money'):
  13. print(getattr(a,'money'))

执行输出:

>


500

类使用类命名空间中的名字

对象使用对象能用的方法和属性

模块使用模块中的名字

import os ; getattr(os,’rename’)(‘user’,’user_info’)

从自己所在的模块中使用自己名字

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

执行输出:

1524044098.9708395

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

  1. class Teacher:
  2. todo_list = [
  3. ]
  4. def __init__(self,name):
  5. self.name = name

导入模块 teacher

  1. import teacher
  2. t = 'Teracher'
  3. alex = teacher.t('alex').name
  4. print(alex)

执行输出:alex

  1. import teacher
  2. t = 'Teacher'
  3. Terach_class = getattr(teacher,t)
  4. print(Terach_class)
  5. print(teacher.Teacher)

执行输出:

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

第一种:常规办法

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

第二种:使用反射

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

怎么反射自己的模块名?

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

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

完整代码如下:

  1. def login():
  2. print('执行login功能')
  3. def register():
  4. print('执行register功能')
  5. import sys
  6. #print(sys.modules['__main__']) # 找到自己所在的模块(py文件)
  7. func = input('>>>')
  8. 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

  1. class A:
  2. def __init__(self,name):
  3. self.name = name
  4. def wahaha(self):
  5. print('wahaha')
  6. a = A('alex')
  7. a.name
  8. a.wahaha()

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

  1. class A:
  2. def __init__(self,name):
  3. self.name = name
  4. def wahaha(self):
  5. print('wahaha')
  6. a = A('alex')
  7. print(a.__dict__)
  8. setattr(a,'age',18) # 给a对象新增一个属性
  9. print(a.__dict__)

执行输出:

{‘name’: ‘alex’}

{‘name’: ‘alex’, ‘age’: 18}

再增加一个属性

  1. class A:
  2. def __init__(self,name):
  3. self.name = name
  4. def wahaha(self):
  5. print('wahaha')
  6. a = A('alex')
  7. print(a.__dict__)
  8. setattr(a,'age',18) # 给a增加一个属性
  9. print(a.__dict__)
  10. setattr(a,'name','egon') # 修改name属性
  11. print(a.__dict__)

执行输出:

{‘name’: ‘alex’}

{‘name’: ‘alex’, ‘age’: 18}

{‘name’: ‘egon’, ‘age’: 18}

删除一个属性

  1. class A:
  2. def __init__(self,name):
  3. self.name = name
  4. def wahaha(self):
  5. print('wahaha')
  6. a = A('alex')
  7. delattr(a,'name')
  8. print(a.__dict__)

执行输出:{}

很少有删除一个方法的

上面都是倒腾属性的,

下面说增删改方法,非常非常不常用

  1. class A:
  2. def __init__(self,name):
  3. self.name = name
  4. def wahaha(self):
  5. print('wahaha')
  6. def qqxing(self):
  7. print('qqxing')
  8. a = A('alex')
  9. setattr(a,'qqxing',qqxing) # 增加一个属性
  10. print(a.__dict__)<br>a.qqxing

执行输出:

{‘name’: ‘alex’, ‘qqxing’: }

不能 set 给对象添加一个方法

否则 a.qqxing()就报错

这样才行

  1. class A:
  2. def __init__(self,name):
  3. self.name = name
  4. def wahaha(self):
  5. print('wahaha')
  6. def qqxing(self):
  7. print('qqxing')
  8. a = A('alex')
  9. setattr(a,'qqxing',qqxing) # 增加一个属性
  10. # print(a.__dict__)
  11. a.qqxing(a)

执行输出:qqxing

面试也不会问 set 方法

方法可以绑定给类

但是不能绑定给对象

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597655199217-75c0d3ca-8d6b-45bd-9149-4ae1361228f1.png)

重点记住:

getattr

hasattr

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

实例化一个 list

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

li 是对象,list 是类

查看长度,使用 len 方法

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

执行输出:

[1, 2, 3, 4, 5]

5

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

  1. class A:
  2. def __init__(self,name,age,sex,cls):
  3. self.name = name
  4. self.age = age
  5. self.sex = sex
  6. self.cls = cls
  7. a = A('alex',81,'不详',2)
  8. print(len(a))

执行输出:

TypeError: object of type ‘A’ has no len()

那么非要计算长度呢?

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

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

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

执行输出:

5

多实例化几次呢?

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

执行输出:

5

5

5

现在想要len有实际意义

统计属性的个数

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

执行输出:

5

4

4

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

可哈希和不可哈希

字典的寻址速度是最快的

它用的是哈希算法

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

字典的 key 不能一样

一个对象,能不能 hash

靠一个内置方法hash决定的

  1. class A:
  2. def __init__(self,name,age,sex,cls):
  3. self.name = name
  4. self.age = age
  5. self.sex = sex
  6. self.cls = cls
  7. a1 = A('alex',81,'不详',2)
  8. print(hash(a1))

执行输出:

141908978608

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

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

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

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

可以自己定义个hash方法

  1. class A:
  2. def __init__(self,name,age,sex,cls):
  3. self.name = name
  4. self.age = age
  5. self.sex = sex
  6. self.cls = cls
  7. def __hash__(self):
  8. return 0
  9. a1 = A('alex',81,'不详',2)
  10. print(hash(a1))

执行输出:0

明日默写:

  1. class A:
  2. role = 'Person'
  3. def __init__(self):
  4. self.money = 500
  5. def func(self):
  6. print('*'*10)
  7. a = A()
  8. print(a.func)
  9. getattr(a,'func')()
  10. print(getattr(a,'money'))

还有一个

  1. def login():
  2. print('执行login功能')
  3. def register():
  4. print('执行register功能')
  5. import sys # 和python解释器相关的内容都在sys里
  6. print(sys.modules['__main__'])
  7. func = input('>>>')
  8. if hasattr(sys.modules['__main__'],func):
  9. getattr(sys.modules['__main__'],func)()