Python--面向对象进阶
isinstance和issubclass
isinstance
isinstance(obj1,obj2):判断对象与类之间的关系,判断第一个参数是否是第二个参数的实例。
>>> n1 = 10 >>> isinstance(n1, int) #判断n1是否是数字类型,如果是返回true如果不是防护false true >>> class a(object): ... pass ... >>> a1 = a() >>> isinstance(a1, a) # 判断a1是否是类a的对象,如果是返回true,如果不是返回false true
type()函数和isinstance()函数两者有什么区别呢?
>>> print(type(1) is int) true >>> print(isinstance(1, int)) true #从上面的结果看,两者的结果都是true,那么type()与isinstance()的区别在哪呢? #从接下来的例子,就能够清晰看出来。 class a:pass class b(a):pass b = b() print(isinstance(b, a)) # true print(isinstance(b, b)) # true print(type(b)) # <class '__main__.b'> print(type(b) is b) # true print(type(b) is a) # false #总结: isinstance()是可以用在继承的关系上;而type()不能用来检测继承关系。
issubclass
issubclass(obj1,obj2):用来描述一个类与另一个类之间的关系,判断一个类是否是另一个类的子类
class a:pass class b(a):pass print(issubclass(b, a)) # true print(issubclass(a, b)) # false # 总结: 类b是类a的子类,类a不是类b的子类
反射
什么是反射?
反射的概念是由smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在lisp和面向对象方面取得了成绩。
python中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数:hasattr getattr setattr delattr
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
def hasattr(*args, **kwargs): # real signature unknown """ return whether the object has an attribute with the given name. this is done by calling getattr(obj, name) and catching attributeerror. """ pass
def getattr(object, name, default=none): # known special case of getattr """ getattr(object, name[, default]) -> value get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. when a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
def delattr(x, y): # real signature unknown; restored from __doc__ """ deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass
类使用反射
类:静态属性 类方法 静态方法
class student(object): name = "小白" @classmethod def check_course(cls): print("查看课程") @staticmethod def login(): print("登录") # 检查是否含有某属性 print(hasattr(student, "login")) # 检测student类中是否有login方法,如果有返回true,没有返回false # 通过反射查看静态属性 print(getattr(student, 'name')) # 通过反射调用方法 print(getattr(student, "check_course")) # 得到的是类方法的内存地址 getattr(student, "check_course")() # 调用类方法 print(getattr(student, "login")) # 得到的是静态方法的内存地址 getattr(student, "login")() # 调用静态方法 # 一般情况hasattr和getattr联用, 示例: num = input('>>>') # 等待用户输入 if hasattr(student, num): # 判断用户输入的方法是否有,如果有才执行。 getattr(student, num)()
对象使用反射
class a(object): def __init__(self, name): self.name = name def func(self): print(" in func ") a = a("小白") print(a.name) print(getattr(a, "name")) getattr(a, "func")()
模块使用反射
import os os.rename('__init__.py', 'init') getattr(os, 'rename')('init', '__init__.py') # os.rename('init', '__init__.py')
自定义模块使用反射
import sys def wahaha(): print("wahaha") def qqxing(): print("qqxing") wahaha() qqxing() my_file = sys.modules['__main__'] getattr(my_file, 'wahaha')() getattr(my_file, 'qqxing')()
反射总结
# 反射 # hasattr, getattr # 类名.名字 # getattr(类名, '名字') # 对象名.名字 # getattr(对象, '名字') # 模块名.名字 # import 模块 # getattr(模块, '名字') # 自己文件.名字 # import sys # getattr(sys.modules['__main__'], '名字')
反射的应用
#! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "yanjieli" # date: 2018/11/25/025 import sys class int(object): def __init__(self, name): self.name = name class manager(int): operate_list = [ ('创建课程', 'create_course'), ('招聘老师', 'create_teacher'), ('查看课程', 'check_course'), ('查看学生信息', 'check_userinfo'), ] def create_course(self): print("创建课程") def create_teacher(self): print("招聘老师") def check_course(self): print("查看课程") def check_userinfo(self): print("查看学生信息") class teacher(int): operate_list = [ ('查看课程', 'check_course'), ('查看学生成绩', 'check_achievement'), ] def check_course(self): print("查看课程") def check_achievement(self): print("查看学生成绩") class student(int): operate_list = [ ('查看课程', 'check_course'), ('选择课程', 'choose_course'), ('查看已选课程', 'choosed_course'), ('jsdklfjskld', 'aa'), ] def check_course(self): print("查看课程") def choose_course(self): print("选择课程") def choosed_course(self): print("查看已选课程") def login(): username = input('user:>>>') password = input('password:>>>') with open("user_info", encoding="utf-8") as f: for line in f: user, pwd, ident = line.strip().split("|") if username == user and password == pwd: print("欢迎您!%s" % user) return user, ident else: print("登录失败") exit() def main(): user, id = login() cls = getattr(sys.modules['__main__'], id) # 通过反射拿到当前登录用户所属的类,如果是学生,则拿到学生类; operate_list = cls.operate_list obj = cls(user) for index, i in enumerate(operate_list, 1): print(index, i[0]) # 打印出所有的功能 option = int(input("option: >>>")) option_itme = operate_list[option -1] # 拿到的是一个元组,比如:('创建课程', 'create_course'), getattr(obj, option_itme[1])() # 通过反射拿到所输入的选项对应的方法并执行 main()
内置方法
内置方法:内置方法就是不需要程序员定义,本身就存在类中的方法。内置方法又称为双下方法,魔术方法。
内置方法通常长成这样:__名字__。 每一个双下方法都有它自己的特殊意义
所有的双下方法,都不需要我们直接调用,都有另外一种自动触发它的方法。而是总有一些其他的 内置方法 特殊的语法 来自动触发这些 双下方法
__call__
__call__ 方法的执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用。
class person: def __init__(self, name, age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('调用对象的__call__方法') a = person('张三', 24) # 类person可调用 a() # 对象a可以调用
class a(object): def __call__(self, *args, **kwargs): print("执行了call方法") class b(object): def __init__(self, cls): print("在实例化a之前做一些事情") self.obj = cls() self.obj() print("在实例化a之后做一些事情") a = a() a() # 对象() == 相当于调用__call__方法 a()() # 类名()() 相当于先实例化得到了一个对象,再对对象(), ==> 和上面的结果一样,相当于调用__call__方法 b(a)
__len__
计算对象的长度,如果类中定义了__len__方法,那么在对象中就能用内置函数len(obj),就会自动调用__len__方法。
class foo(object): def __init__(self, s): self.s = s def __len__(self): print('调用了__len__方法, 计算长度') return len(self.s) a = foo("aaaa") print(len(a)) # 自动调用了__len__方法 ''' 打印结果: 调用了__len__方法, 计算长度 4 '''
__new__
网上经常有一个笑话“程序员可以自己new一个对象”, 到底new有什么作用呢?
__new__又称为构造方法,通过__init__()方法,我们知道初始化一个实例需要经历的几个步骤中的第一步就是在内存空间中开辟一个对象空间,这个对象空间是__init__方法开辟的么?其实不是,在init之前,实例化对象的第一步是通过__new__方法创建一个对象空间。
class fuu(): def __new__(cls, *args, **kwargs): # 构造方法,构造了对象的空间 print("执行了__new__方法") return object.__new__(cls) # 或者super().__new(cls) # 调用了object类中的__new__方法 def __init__(self, name, age): # 初始化方法 print("执行了init方法") self.name = name self.age = age c1 = fuu("小白", 18) """ 就这样执行可以打印出: 执行了__new__方法 执行了init方法 """
从上面的例子,我们可以更加清晰的得到初始化一个对象所经历的过程:
1、实例化fuu类,应该先执行其__new__方法。
2、但是fuu类中没有定义__new__方法,所以到object父类中执行object的__new__方法。
回顾调用父类的两种方法:
object.__new__(cls)
super().__new__(cls)
3、此时在内存中开辟一块对象空间。
4、才执行init初始化方法,把地址传给self,给对象封装属性。
总结:构造方法__new__和初始化方法__init__之间的区别?
形象的说,类好比一个人类型的模板,__new__构造方法就是捏小人的过程(捏出来的每一个小人都是一样的),__init__初始化方法好比给小人穿衣服的过程(为对象封装属性),一定是先有__new__方法,后有__init__。
单例模式:一个类只有一个实例的时候,这种就叫做单例模式。
# 单例模式:一个类只有一个实例。 # 思考:如果使得一个类只能实例化一次,也就是这要控制只能开辟一次空间,就能实现单例模式。 class singleinstance(): __instance = none # 通过设置一个标志位,用来使得只能运行一次new方法 def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance def __init__(self, name, age): self.name = name self.age = age obj1 = singleinstance('小白', 18) obj2 = singleinstance('小黑', 20) print(obj1.name, obj1.age) print(obj2.name, obj2.age) print(obj1) print(obj2) >>> 小黑 20 小黑 20 <__main__.singleinstance object at 0x000001c1657f6048> <__main__.singleinstance object at 0x000001c1657f6048> ''' 总结:由上打印可以看到不论实例化多少对象,都是一个对象,都会已最后一个为准。 '''
__str__ 与 __repr__
__str__
如果想把一个类的实例变成str类型,打印对象名的时候就执行__str__方法。
__str__ : str(obj),要求必须实现了__str__,要求这个方法的返回值必须是字符串str类型
三种场景会触发__str__方法:
- (1)当你打印一个对象名的时候,就会就会触发__str__。
- (2)当你使用%s格式化输出对象的时候,也会触发__str__。
- (3)强制转换数据类型的时候,也会触发__str__。
class student: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __str__(self): return "%s %s %s" % (self.name, self.age, self.sex) st1 = student("小白", 18, "男") print(st1) #1.打印对象名,自动触发__str__方法 >>> 小白 18 男 student_list = [] student_list.append(st1) for index, i in enumerate(student_list): print('%s %s'% (index, i)) #2.当使用%s格式化的时候,自动触发__str__ >>> 0 小白 18 男
__repr__
1、__repr__是__str__方法的备胎,如果有__str__就使用__str__,否则执行__repr__。
# (1)同时存在__str__和__repr__两个方法: class student: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __str__(self): return "str: %s %s %s" % (self.name, self.age, self.sex) def __repr__(self): return "repr: %s %s %s" % (self.name, self.age, self.sex) st1 = student("小白", 18, "男") print(st1) >>> str: 小白 18 男 # (2)只存在__repr__方法时,再次打印对象名: class student: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 注释掉__str__双下方法 # def __str__(self): # return "str: %s %s %s" % (self.name, self.age, self.sex) def __repr__(self): return "repr: %s %s %s" % (self.name, self.age, self.sex) st1 = student("小白", 18, "男") print(st1) # 当__str__没有时,执行__repr__方法 >>> repr: 小白 18 男
如果__repr__仅仅只是__str__的备胎,那么它就没有存在的意义了。所有__repr__还是有它自己的用武之地的时候:
2、如果使用内置函数repr(obj),或者通过%r格式化的时候,就会自动触发__repr__方法,不管有没有__str__,都调用__repr__。
st1 = student("小白", 18, "男") print(repr(st1)) >>> repr: 小白 18 男
小技巧:
python中之所有既有__str__,又有__repr__,__str__用于显示给用户看,__repr__用于显示给开发人员看。 下面就有一个偷懒的小办法:
__str__=__repr__
扩展知识:
class fcc: def __str__(self): return "fcc.str" def __repr__(self): return "fcc.repr" class ecc(fcc): def __str__(self): return "ecc.str" def __repr__(self): return "ecc.repr" e1 = ecc() print(e1) >>> ecc.str ''' (1) 先去子类中查找,先调用__str__方法。 (2) 如果把子类的__str__方法注释掉,会去父类中查找父类的__str__方法 (3) 如果把父类的__str__的方法注释掉,会再回到子类中执行备胎__repr__方法。 '''