python 全栈开发,Day23(复习,isinstance和issubclass,反射)
一、复习
1 2 3 4 5 6 7 8 9 | 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实例化时,会将里面的方法与做类做绑定。
1 2 3 4 5 6 7 8 9 10 11 12 | 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'>>
类方法 由于不适用对象内存空间中的属性,所以不会将对象和方法绑在一起
而是将类和方法绑在一起
1 2 3 4 5 6 7 8 9 10 11 | 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的对象
派生
1 2 3 4 5 6 7 8 | 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}
这里面没有派生属性(子类有,父类没有的属性)
增加一个派生属性
1 2 3 4 5 6 7 8 9 | 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}
第二种方式,添加派生属性
1 2 3 4 5 6 7 8 9 10 11 12 | 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重新赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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()。
1 2 | a = 2 print ( isinstance (a, int )) # 判断是否为整形 |
执行输出: True
判断继承关系
1 2 3 4 5 6 7 8 | 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 检测子类,父类关系
1 2 3 4 | class C: pass class D(C): pass print ( issubclass (C,D)) print ( issubclass (D,C)) |
执行输出:
False
True
issubclass(子类名,父类名) 如果返回True,说明有继承关系
多继承
1 2 3 4 5 6 7 8 | 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
1 2 | role = 123 print ( eval ( 'role' )) |
执行输出: 123
1 2 3 | class A: role = 'Person' print (A.__dict__[ 'role' ]) |
执行输出:
Person
根据输入内容,测试获取的类的属性
1 2 3 4 | class A: role = 'Person' ret = input ( '>>>' ) print (A.__dict__[ret]) |
执行输出:
>>>role
Person
那么问题来,如果获取一个函数呢?
1 2 3 | def func(): pass ret = input ( '>>>' ) eval (ret) |
如果植入一段代码,删除根目录,那么linux系统就完了。。。
1 | import os;os.remove( '/' ) |
所以eval不能随便用,这样写没有安全意识
input的内容,没法估量,有可能是病毒或者恶意代码
hasattr和getattr的组合使用
1 2 3 4 5 6 | class A: role = 'Person' if hasattr (A, 'role' ): # 判断role属性在A中是否存在 print ( getattr (A, 'role' )) # 获取role属性的值 |
执行输出:
Person
getattr 使用字符串数据类型的变量名 访问一个命名空间中的名字
前提是,必须指定类的命名空间
比如A,'role'
如果getattr获取不到值,直接报错,所以一般和hasattr配合使用
类中的方法也是可以的获取的
从A的命名空间里找一个方法 ,找到的是这个方法的内存地址
1 2 3 4 5 6 7 | 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>
1 2 3 4 5 6 7 8 9 10 11 12 | 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() 从命名空间中获取这个名字对应的值
类中的反射
类可以获取类中的属性和方法
1 2 3 4 5 6 7 8 | 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就可以获取了。
1 2 3 4 5 6 7 8 9 | 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
*
看这一句代码:
1 | func = getattr (A,ret) |
等式计算右边的,getattr获取的值为A类func方法的内存地址。那么将值赋予给func之后,
外部的func和类内部的func名字是同名的,此时左边的fun和A类的方法名重名。但是不冲突,为什呢?因为一个在类外部有,一个在类的外表不
加打印
1 2 3 4 5 6 7 8 9 10 11 | 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) |
查看内存地址:
1 2 3 4 | >>>func <function A.func at 0x000001E9CF8CAA60 > * <function A.func at 0x000001E9CF8CAA60 > |
内存地址是一样的
小练习,使用反射的知识,用对象调用func
1 2 3 4 5 6 | class A: role = 'Person' def func( self ): print ( '*' * 10 ) a = A() |
答案:
1 2 3 4 5 6 7 8 9 | 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判断,就可以执行对应的方法了。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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')
从自己所在的模块中使用自己名字
1 2 3 4 5 | import time # 一个py文件就是一个模块 time.time() if hasattr (time, 'time' ): print (time.time) print ( getattr (time, 'time' )()) |
执行输出:
<built-in function time>
1524044098.9708395
新建文件teacher.py 内容如下:
1 2 3 4 5 6 | class Teacher: todo_list = [ ] def __init__( self ,name): self .name = name |
导入模块teacher
1 2 3 4 | import teacher t = 'Teracher' alex = teacher.t( 'alex' ).name print (alex) |
执行输出:alex
1 2 3 4 5 | import teacher t = 'Teacher' Terach_class = getattr (teacher,t) print (Terach_class) print (teacher.Teacher) |
执行输出:
<class 'teacher.Teacher'>
<class 'teacher.Teacher'>
假设有一个文件名userinfo,需要重命名
第一种:常规办法
1 2 | import os os.rename( 'userinfo' , 'user' ) |
第二种:使用反射
1 2 | import os<br> #os.rename('userinfo','user') getattr (os, 'rename' )( 'user' , 'user_info' ) |
1 2 3 4 5 6 7 | a = 1 b = 2 def login(): print ( '执行login功能' ) def register(): print ( '执行register功能' ) func = input ( '>>>' ) |
怎么反射自己的模块名?
使用sys模块,和python解释器相关的内容都在sys里
1 2 | import sys print (sys.modules[ '__main__' ]) # 查看当前文件路径,包含文件名 |
完整代码如下:
1 2 3 4 5 6 7 8 | 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
1 2 3 4 5 6 7 8 | class A: def __init__( self ,name): self .name = name def wahaha( self ): print ( 'wahaha' ) a = A( 'alex' ) a.name a.wahaha() |
可以给a实例,绑定一个属性age
1 2 3 4 5 6 7 8 9 | 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}
再增加一个属性
1 2 3 4 5 6 7 8 9 10 11 | 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}
删除一个属性
1 2 3 4 5 6 7 8 | class A: def __init__( self ,name): self .name = name def wahaha( self ): print ( 'wahaha' ) a = A( 'alex' ) delattr (a, 'name' ) print (a.__dict__) |
执行输出:{}
很少有删除一个方法的
上面都是倒腾属性的,
下面说增删改方法,非常非常不常用
1 2 3 4 5 6 7 8 9 10 11 | 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()就报错
这样才行
1 2 3 4 5 6 7 8 9 10 11 12 | 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
1 2 | li = list ([ 1 , 2 , 3 , 4 , 5 ]) print (li) |
li是对象,list是类
查看长度,使用len方法
1 2 3 | li = list ([ 1 , 2 , 3 , 4 , 5 ]) print (li) print ( len (li)) |
执行输出:
[1, 2, 3, 4, 5]
5
那么类,可以使用len方法吗?
1 2 3 4 5 6 7 8 | 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()方法时。
1 2 3 4 5 6 7 8 9 10 11 12 | 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
多实例化几次呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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__有实际意义
统计属性的个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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__决定的
1 2 3 4 5 6 7 8 9 | 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__方法
1 2 3 | def __hash__( self , * args, * * kwargs): # real signature unknown """ Return hash(self). """ pass |
由于python3所有类都继承了object类,所以a1对象之所以可以hash,是靠object类来完成的。
可以自己定义个__hash__方法
1 2 3 4 5 6 7 8 9 10 11 12 | 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
明日默写:
1 2 3 4 5 6 7 8 9 10 11 | 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' )) |
还有一个
1 2 3 4 5 6 7 8 9 10 11 | 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)() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix