面向对象编程之获取对象信息:type()、isinstance()、dir()、hasattr()、getattr()、setattr()等等

阅读前请看一下:我是一个热衷于记录的人,每次写博客会反复研读,尽量不断提升博客质量。文章设置为仅粉丝可见,是因为写博客确实花了不少精力。希望互相进步谢谢!!

前言

time:2022/06/10
记录学习python的笔记 ,有知识点,也有评论区的精华,还有自己的一些疑问。
具体文章见:https://www.liaoxuefeng.com/wiki/1016959663602400/1017499532944768?t=1654821830104#0


提示:以下是本篇文章正文内容

一、笔记

1、type()、isinstance()、dir()、hasattr()、getattr()、setattr()共性?

答:都是针对类实例化后的对象而言,是为了搞清楚对象的一些特性。故第一个参数永远是实例化后的对象,而非类。


2、isinstance() 与 type() 区别:
  • type() 不会认为子类是一种父类类型,不考虑继承关系

  • isinstance() 会认为子类是一种父类类型,考虑继承关系

  • 如果要判断两个类型是否相同推荐使用 isinstance()

    #继承关系:Animal-->Dog
    print(type(dog) == Animal)    #False,因为不考虑继承
    print(type(dog) == Dog)    #True
    print(isinstance(dog, Animal))   #True,因为考虑继承
    print(isinstance(dog, Dog))    #True 
    
  • 使用isinstance还可以判断一个变量是否为某些类型中的一种,如下代码:

    print(isinstance([1, 2, 3], (list, tuple)))
    #Output: True
    isinstance((1, 2, 3), (list, tuple))
    #Output: True
    

3、types模块:
  • 用途:判断一个对象是否为函数。
  • 使用:
    import types
    def fn():
        pass
    print(type(fn)==types.FunctionType)   
    #输出:True
    print(type(abs)==types.BuiltinFunctionType)  
    #输出:True
    print(type(lambda x: x)==types.LambdaType)   
    #输出:True
    print(type((x for x in range(1, 10))) == types.GeneratorType)   
    #输出:True
    

4、dir():
  • 用途:返回一个list,里面包含了该对象的所有属性与方法

  • 使用:

    print(dir('Abc'))
    #Output: ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
    

    上面代码说明:类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

    print(len('ABC'))
    #Output: 3
    print('ABC'.__len__())
    #Output: 3
    

    剩下的都是普通属性或方法,比如lower()返回小写的字符串:

    print('ABC'.lower())
    #output: 'abc'
    

5、hasattr()、getattr()、setattr():
  • 用途:

    • hasattr(object, name)判断一个对象里面是否有name属性或者name方法。
    • getattr(obj,name[,default)函数用于返回一个对象属性值
    • setattr(obj,name,value)函数对应函数getattr(),用于设置属性值,该属性不一定是存在的。
  • 这三个函数仍然是外部程序对实例对象的属性的调用,故不可用来处理私有变量

  • 相对于object.attribution的直接调用,上诉函数的优点在于第二个参数除了是字符串外(既可以是属性名,又可以是方法名,但一定注意一定要加‘’),还可以当作变量,具体见如下代码:

    #把dict的key - value复制到obj,前提是obj有对应得property
    def dict2bean(obj, d):
    	for k, v in items(d):
    		if hasattr(obj, k):
    			setattr(obj, k, v) # 能写成obj.k = v吗?
    

    那里一定不能写成obj.k = v。例如setattr(obj,‘y’,19) 中的‘y’,此时并不是一个变量,它只是一个字符串,起着匹配的作用;而代码中setattr(obj, k, v)的k虽然也是匹配作用,但此时并非一个简单的字符串,而是一个变量,指向每次循环对应的dic中的key值,若写成obj.k = v这个,obj中就会生成k这个属性,故不行。

  • 只有在不知道对象信息的时候我们才去获取对象信。
    如果可以直接写:

    sum = obj.x + obj.y
    

    就不要写:

    sum = getattr(obj, 'x') + getattr(obj, 'y')
    

    一个正确的用法的例子如下:

    def readImage(fp):
    	    if hasattr(fp, 'read'):
    	        return readData(fp)
    	    return None
    

    假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。hasattr()就派上了用场。

    请注意,在Python这类动态语言中,根据鸭子类型,有read()方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要read()方法返回的是有效的图像数据,就不影响读取图像的功能。


6、定义类时属性、参数值傻傻分不清楚?
  • 如下代码中:aa,bb才是对象的属性,name,score只是参数,是用来传值的。
    class myobject():
    	def __init__(self, name, score):
    	self.aa = name
    	self.bb = score
    
  • 只是我们习惯这样写,参数名和属性名相同,若要定义为私有属性,在属性前加两个下划线,代码如下:
    class myobject():
    	def __init__(self, name, score):
    	self.name = name        #公有属性:name
    	self.__score = score    #私有属性:__score
    ```- 
    

7、定义类时,初始化函数定义的不同,导致实例化对象的方式也不同
  • 第一种含self以外参数的初始化函数,定义如下:

    class MyObject(object):
        def __init__(self, name, score):
            self.name = name
            self.__score = score
    
    test = MyObject('Michael', 100)
    
    #print(test.name, test.__score)  
    #错误写法,__score为私有属性,外接无法访问,故报错
    print(test.name)
    #Output: Michael  
    
    test.name = 'Bob'
    #修改名字后覆盖,注意__score为私有属性,不能外部修改
    print(test.name)
    #Output: Bob
    

    由于初始化函数含有name、__score两个参数,故初始化时需要传入两个参数。

  • 第二种只含self参数的初始化函数,定义如下:

    class MyObject(object):
        def __init__(self):
            self.name = 'Michael'    #这里一定要初始化,否则报错
            self.__score = 100      #这里一定要初始化,否则报错
    
    #test = MyObject('Michael', 100)
    #错误写法,由于初始化只有self参数,故不能传参
    test = MyObject()
    
    #print(test.name, test.__score)  
    #错误写法,__score为私有属性,外接无法访问,故报错
    print(test.name)
    #Output: Michael  
    
    test.name = 'Bob'
    #修改名字后仍然覆盖,注意__score为私有属性,不能外部修改
    print(test.name)
    #Output: Bob
    
  • 总结:初始化函数若有其他参数,创建实例时就必须传参;否则就不用


8、object和instance的关系:MyObject.run、MyObject().run、MyObject.run()、MyObject().run()四者区别
  • 示例代码如下:
	class MyObject(object):
	    def __init__(self):
	        self.name = 'Michael'  #这里一定要初始化,否则报错
	        self.__score = 100     #这里一定要初始化,否则报错
	    def run(self):
	        print('I am running')
	print(type(MyObject.run))
	#Output: <class 'function'>
	print(type(MyObject().run))
	#Output: <class 'method'>
	print(type(MyObject.run()))
	#报错TypeError,因为run函数定义时有self,self会指向实例,故会报缺少参数的错误,故此时需要先创建实例以后才能调用
	print(type(MyObject().run()))
	#Output:  I am running
	#        <class 'NoneType'>
```
  • 分析上述代码:
    • MyObject.run是function,MyObject().run是method。method与实例关联,调用时会把实例作为参数隐式传递,function不会;

    • MyObject.run()报错,原因见代码注释

    • MyObject().run()是运行实例中的run函数,故首先会打印“I am running”,其次由于没有return语句,故默认返回None,故类型也是“NoneType”;

    • 永远记住,类只是一个模板,模板是不会亲自下海干活的。

      首先区分函数本身和函数调用:python中如果不加括号,除了个别的,那就是函数本身(即函数名、即标识符(你可以理解为变量)),加了括号代表函数调用(即运行前面的东西)。例如内置绝对值函数,abs是函数本身,是函数名,也是一个变量,它指向了函数体这个对象(即定义的数据规则),而abs()是调用执行函数并返回一个值。

      例如单独看MyObject就是一个类名,是一个变量,指向了一个类对象,程序员可以通过MyObjcet去进行操作,而不再是通过地址去操作;再单独看MyObject()是调用这个类创建一个实例(官方解释“如何创建实例?类名+()”也印证了)。

      再单独看run,就是函数本身,是一个函数名,是一个变量,指向了函数对象;而单独看run()就是调用函数。那为何MyObject.run和MyObject().run会返回function和method两种类型呢?

      其实method可以看作是function的一个特例。method指的是对象的关联函数,即必须是针对于创建的实例而言,故必须实例化后才能调用!而function这里指的是类中的函数,再想想最初学面向过程时定义的一般的函数,其类型也是function。

      故MyObject().run()就很好理解了,前面代表创建一个实例,才能下海干活,后面代表调用这个实例中的run函数。

    • 写到这,应该有人会问了,上面代码中,属性都是通过self.xxx定义的,类里面的函数第一个参数也永远是self,可不可以不用self呢?关于这个问题,请看,类属性与实例属性


8、创建实例时一定定义变量进行引用,否则无法对后续进行操作

直接看下面代码:

class MyObject(object):
    def __init__(self):
        self.x = 9     #默认为9
    def power(self):
        return self.x * self.x
    def set_x(self, x):
        self.x = x

#MyObject()就是实例,在没有变量引用MyObject()的情况下,后续的操作(赋值等)其实都没有用
print(MyObject().x)    #结果不变,为9
MyObject().x = 2
print(MyObject().x)    #结果不变,为9
setattr(MyObject(), 'x', 2)
print(MyObject().x)   #结果不变,为9
MyObject().set_x(2)
print(MyObject().x)    #结果不变,为9
print("_____________________________________________")#分割线

#obj=MyObject()就把实例引用给到变量obj,用变量obj可以操作实例,变量obj的值就是实例地址
obj = MyObject() #实例化
print(obj.x)   #默认为9
obj.x = 2
print(obj.x)    #结果变化为2
setattr(obj, 'x', 9)
print(obj.x)    #结果变化为9
obj.set_x(2)
print(obj.x)    #结果变化为2

码字不易,谢谢点赞!!!
码字不易,谢谢点赞!!!
码字不易,谢谢点赞!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值