python学习第19天----内置函数_反射_双下方法_单例类

python学习第19天----内置函数_反射_双下方法_单例类

1.内置函数补充

1)isinstance(对象,类)

作用:用于判断对象属不属于这个类型,继承的类也算(即一个子类的对象,通过isinstance判断它是否属于父类时,返回的也是True)

#判断对象所属类型,包括继承关系

class A: pass
class B(A): pass
b = B()
print(isinstance(b,B))
print(isinstance(b,A))
输出:
True
True
View Code

备注:若【type(子类的对象) is 类】方式判断一个对象是否属于这个类时,只有当前类才返回True,若是父类则返回False

class A: pass
class B(A): pass
b = B()
print(isinstance(b,B))
print(isinstance(b,A))
print(type(b) is A)    #父类则返回False
输出:
True
True
False
True
View Code

总结type()不包含继承关系,只管一层;isinstance,包含所有的继承关系

补充:【==】用来判断内存地址是否相等,【i】s用来判断内存地址是否相同,所以is要求更 苛刻,不仅要求值相等,要要求内存地址相等

2)issunclass(A类,B类)      

作用:判断类于类之间的继承关系;即A类是不是B类的子类

class A():
    pass
class B(A):
    pass
print(issubclass(A,B))
print(issubclass(B,A)) #B是A的子类,返回True
输出:
False
True
View Code

2.反射

1)什么是反射:用字符串数据类型的变量名来访问这个变量的值

2)反射的方法:getattr()  hasattr()  setattr()  delattr()

2)反射的使用

①使用类去反射

格式:

类命名空间.XXX == getattr(命名空间,'XXX')

#引:不使用反射的情况下,编写一个学生选课系统

class Student:
    def __init__(self) :pass
    def check_course(self):
        print("查看课程")
    def choose_course(self):
        print("选择课程")
    def choosed_course(self):
        print("查看已选课程")
s = Student()
content = input(">>>")
if content == "check_course":
    s.check_course()
elif content == "choose_course":
    s.choose_course()
elif content == "choosed_course":
    s.choosed_course()
输入输出:
>>>check_course
查看课程
View Code

问题:对于如上程序,如果学生类中有很多的方法,当根据用户输入,判断哪一个方法执行时,若是有多个方法都需要去一一判断,代码变的非常的冗长;并且如果用户

#通过反射,拿到类的静态变量(即静态字段,也叫静态属性)的值

class Student:
    ROLE = "STUDENT"         #ROLE就是变量
print(Student.ROLE)
a = getattr(Student,'ROLE')       #'ROLE'是字符串类型,即用字符串数据类型的变量名访问到了变量的值
print(a)
输出:
STUDENT
STUDENT
View Code

:对于用户输入的内容和网络传输的内容不能直接用eval()去执行,容易出现安全问题;只有eval()明确的写在自己的代码里,才能去使用

说明:getattr(Student,'ROLE')方法中,第一个参数的命名空间中的变量名为第二个参数的变量的值,即从Student这个类的命名空间中取到ROLE这个变量的值

#通过反射,拿到类的类方法的值

class Student:
    @classmethod
    def check_course(cls):
        print("查看所有课程")
print(getattr(Student,'check_course')) #打印方法的地址值
getattr(Student,'check_course')()     #通过反射调用类方法
输出:
<bound method Student.check_course of <class '__main__.Student'>>
查看所有课程
View Code

#通过反射,拿到类的静态方法的值

class Student:
    @staticmethod
    def login():
        print("用户登录")
print(getattr(Student,'login')) #打印方法的地址值
getattr(Student,'login')()      #通过反射调用静态方法
输出:
<function Student.login at 0x000002317FF39048>
用户登录
View Code

#使用反射的方式根据用户的输入,调用相应的方法(又可能用户输入的是一个没有的东西)

class Student:
    ROLE = "STUDENT"
    @classmethod
    def check_course(cls):
        print("查看所有课程")
    @staticmethod
    def login():
        print("用户登录")
content = input(">>>")
getattr(Student,content)()    #用户输入什么就执行什么,如果输入的内容不存在,程序直接报错
输出:
>>>check_course
查看所有课程
>>>aaa
AttributeError: type object 'Student' has no attribute 'aaa
View Code

#将hasattr()和getattr()一起使用,hasattr()表示输入的字符串在类的命名空间中是否可以找到,能找到返回True,不能找到返回False

class Student:
    ROLE = "STUDENT"
    @classmethod
    def check_course(cls):
        print("查看所有课程")
    @staticmethod
    def login():
        print("用户登录")
content = input(">>>")
print(hasattr(Student,content))
getattr(Student,content)()    #用户输入什么就执行什么,如果输入的内容不存在,程序直接报错
输出:
>>>login
True
用户登录
View Code

②使用对象去反射

#通过反射,拿到对象的对象属性和方法

class A:
    def __init__(self,name):
        self.name = name
    def func(self):
        print("In func...")
a = A("阿狸")
print(getattr(a,'name'))  #通过对象去反射对象属性
getattr(a,'func')()      #通过对象去反射方法
输出:
阿狸
In func...
View Code

③使用模板去反射

#以os模块中的rename方法举例

import os
#os.rename('a1.txt','a2.txt')  #将a1.txt改名为a2.txt
getattr(os,'rename')('a2.txt','a1.txt') #利用反射,将a2.txt改名为a1.txt
print(os.rename)
print(getattr(os,'rename'))
输出:
<built-in function rename>
<built-in function rename>
View Code

总结:os.rename 就相当于getattr(os,'rename')

④反射自己模块中的内容

#正常情况下调用本程序中的函数

def lol():
    print("英雄联盟")
def dnf():
    print("地下城勇士")
lol()
dnf()
输出:
英雄联盟
地下城勇士
View Code

#反射自己模块中的函数

#先找到自己当前文件所在的命名空间

import sys         #导入sys模块。该模块中的所有东西都是和python解释器相关的
print(sys.modules)  #sys.modules表示所有在当前这个python程序导入的模块
print(sys.modules['__main__'])     #查找当前所在模块的内存地址
输出:
'__main__': <module '__main__' from 'E:/python/project/untitled/练习/基础代码练习.py'>, 
表示当前文件的内存地址
View Code

#再通过获取到的当前文件的内存地址,去反射本模块中的函数

def lol():
    print("英雄联盟")
def dnf():
    print("地下城勇士")

import sys
file = sys.modules['__main__']
getattr(file,'lol')()
getattr(file,'dnf')()
输出:
英雄联盟
地下城勇士
View Code

3)setattr()和delattr()

#setattr()给修改属性的值()很少用该方式给一个属性重新赋值

class A:
    def __init__(self,name):
        self.name = name
a = A("阿狸")
setattr(a,'name',"九尾妖狐")
print(a.name)
输出:
九尾妖狐
View Code

#delattr()删除属性的值

class A:
    def __init__(self,name):
        self.name = name
a = A("阿狸")
print(a.__dict__)
delattr(a,'name')
print(a.__dict__)
输出:
{'name': '阿狸'}
{}
View Code

反射总结:

         ①方法:hasattr(),getattr(),setattr()

         ②使用类名的反射:类名.名字 ;getattr(类名,'名字')

         ③使用对象名的反射:对象名.名字;getattr(对象,'名字')

         ③模块的反射:模块名.名字;getattr(模块,'名字')

         ④自己模块的反射:模块名.名字;getattr(sys.modules['__main__'],'名字')

例:学生选课系统

#若不要反射,对于一个学生选课系统,当通过login方法登录时,先判断身份,并且根据身份实例化对象,再根据每个身份对应的类,让用户选择能够做的事,这种方式,代码会很长

通过反射实现

#先在userinfo文件中写入用户的用户名、密码、身份如下
--------------------------------------------------------
jianji|123456|Manager
ali|qwer|Student
guangtou|123com|Teacher
------------------------------------------------------

#代码如下

class Manager:
    OPERATOR_DIC =[
        ("创建学生账号","create_student"),
        ("创建课程信息","create_course"),
        ("查看学生信息","check_student")
    ]
    def __init__(self,name):
        self.name = name
    def create_student(self):
        print("创建学生账号")
    def create_course(self):
        print("创建课程信息")
    def check_student(self):
        print("查看学生信息")
class Student:
    OPERATOR_DIC = [
        ("查看课程", "check_course"),
        ("选择课程", "choose_course"),
        ("查看已选课程", "choosed_course")
    ]
    def __init__(self,name):
        self.name = name
    def check_course(self):
        print("查看课程")
    def choose_course(self):
        print("选择课程")
    def choosed_course(self):
        print("查看已选课程")

def login():
    username = input("请输入用户名:")
    password = input("请输入密码:")
    with open("userinfo") as f:
        for line in f:
            user,passwd,ident = line.strip().split("|")
            if username == user and password == passwd:
                print("登录成功")
                return username,ident
import sys
def main():
    usr,id = login()        #login函数的返回值,即jianji Manager
    file = sys.modules['__main__']    #获取当前模板,即获取到当前文件的命名空间;相当于cls就是Manager类;如果是学生登录,cls就是Stuent类
    cls = getattr(file,id)    #等价于getattr(file,'Manager')
    obj = cls(usr)                #实例化一个用户对象,相当于obj = Student(jianji)
    # print(usr,id)
    # print(cls)
    operator_dic = cls.OPERATOR_DIC
    while 1 :
        #通过如下代码,可适用类中的所有方法
        for num,i in enumerate(operator_dic,1):
            print(num,i[0])
        choice = int(input("请输入你的选择>>>"))
        choice_item = operator_dic[choice-1]  #此处获取到的是一个字符串,只能使用getattr()的方式

        getattr(obj,choice_item[1])()

main()

输入输出:
请输入用户名:jianji
请输入密码:123456
登录成功
1 创建学生账号
2 创建课程信息
3 查看学生信息
请输入你的选择>>>1
创建学生账号
View Code

3.双下方法(内置方法)

补充:

         类似【__名字__】的方法叫做类的特殊方法、也叫做双下方法、也叫内置方法、也叫魔术方法(magic_method)

         类中的每一个双下方法都有它自己的特殊意义

1)【__call__】

#对象()就相当于调用__call__方法

class A:
    def __call__(self, *args, **kwargs):
        print("执行__call__方法")
a = A()
a()      #执行__call__方法
A()()   #和上面结果一样,相当于调用 __call__方法(即先实例化一个对象,再对)
输出:
执行__call__方法
执行__call__方法
View Code

 #B类调用A类的__call__方法(很多源码这么用)

class A:
    def __call__(self, *args, **kwargs):
        print("执行__call__方法")
class B:
    def __init__(self,cls):
        self.a = cls()     #②cls()就相当于A(),即创建了一个A类对象,赋值给本类的属性a
        self.a()            #即通过A类的对象,调用call方法
B(A)      #①把B当作参数传给A
输出:
执行__call__方法
View Code

2)【__len__】

补充:len()方法可计算列表、元组等数据类型的长度

list = [2,3,4,7,6,1,6]
tup = (1,2,3,4,5)
print(len(list))
print(len(tup))
输出:
7
5
View Code

#len(obj)就相当于调用了这个对象的__len__方法

class list:
    list = [1,2,3,4,5]
    def __len__(self):
        print("执行len方法。。。")
        return 3  #__len__方法的return值就是len函数的返回值
li = list()
print(len(li))     #len(对象)实际就相当于调用了obj的__len__方法
输出:
执行len方法。。。
3
View Code

总结:__len__方法的return值就是len函数的返回值;如果一个obj对象没有__len__方法,那么len函数就会报错;即要使用len(obj)方法,必须再类中有__len__方法

#可自己定义对象的长度如何计算

class list:
    list = [1,2,3,4,5]
    tup = (1,2,3)
    def __len__(self):
        return len(self.tup)
li = list()
输出:
3
View Code

总结:内置函数和类的内置方法是有很大关系的

练习:写一个类,它有一个字符串类型的属性,要求可统计类的obj对象的长度,该对象的长度就是字符串的长度

class str_len:
    def __init__(self):
        self.s = "我爱北京*"
    def __len__(self):
        return len(self.s)
s = str_len()
print(len(s))
输出:
7
View Code

3)【__new__】

引:__init__是一个初始化方法,它不是构造方法;__new__才是构造方法

补充:实例化一个对象的过程:

         ①先开辟一个属于对象的空间

         ②把对象的空间传给self,然后执行init方法

         ③将这个对象的空间返回给调用者

        1)实例化一个对象时,类本身是不能开辟内存空间空间的;开辟内存空间是__new__方法;因为所有的类都继承了object类,而object类中有一个__new__方法

    ①即先执行本类的__new__方法开辟对象空间,如果本类没有,只能调用object的__new__方法开辟空间

         ②把对象的空间传给self,然后执行init方法

         ③将这个对象的空间返回给调用者

class A:
    def __new__(cls, *args, **kwargs):       #此时还没有对象空间,所以只能传类空间
        obj = object.__new__(cls)
        print("执行new方法")
        print(obj)        #<__main__.A object at 0x00000224717C5EB8>
        return obj
    def __init__(self):
        print(self)     #<__main__.A object at 0x00000224717C5EB8>
        print("执行init方法")
a = A()
输出:

执行new方法
<__main__.A object at 0x00000224717C5EB8>
<__main__.A object at 0x00000224717C5EB8>
执行init方法
View Code

总结:__new__方法在实例化对象之后,但是在__init__方法之前执行

4)【__str__】

4)【__str__】

#print()一个对象,相当于调用一个对象的__str__方法;没有自定义__str__方法仍然可以打印内容是因为object类中实现了。所以打印的内容是一个内存地址,保证打印不出错,但是object类却不知道自定义类中的属性,所以可通过自定义__str__方法实现

class Student:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def __str__(self):     #打印对象,自动调用__str__方法
        return "%s %s %s" %(self.name,self.age,self.sex)
s1 = Student("阿狸","18","")
s2 =Student("泽拉斯","500","")
print(s1)
print(s2)
输出:
阿狸 18 女
泽拉斯 500 男
View Code

#str(obj),就相当于执行obj.__str__方法(即str(obj)得到__str__的返回值)

class Student:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def __str__(self):     #打印对象,自动调用__str__方法
        return "%s %s %s" %(self.name,self.age,self.sex)
s = Student("阿狸","18","")
print(str(s))     #str(obj)相当于执行__str__然后得到其返回值
View Code

# '%s' % obj 也相当于执行__str__方法,并得到其返回值

class Student:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def __str__(self):     #打印对象,自动调用__str__方法
        return "%s %s %s" %(self.name,self.age,self.sex)
s = Student("阿狸","18","")
print("%s" % s)       #【'%s' % obj】就相当于执行__str__方法,并获取其返回值
输出:
阿狸 18 女
View Code

4.单例类

1)定义:如果一个类,从头到尾只能有一个实例,那么这个类就是一个单例类(即只有一块内存空间),所以需要通过__new__方法实现

#当程序走到__init__方法时,空间已经开辟完了,所以要在__init__方法前的__new__方法就要自己定义开辟的空间,而不是继承object类的__new__方法

#不管实例化多少次,所有的对象指向同一块内存空间,所以最后一次的值,就是最终的值

class Single:
    __ISINSTANCE=None
    def __new__(cls, *args, **kwargs):
        if not cls.__ISINSTANCE :
            cls.__ISINSTANCE = object.__new__(cls)
        return cls.__ISINSTANCE
    def __init__(self,name,age):
        self.name = name
        self.age = age
s1 = Single("阿狸",18)
s2 = Single("埃利亚",20)
print(s1.name,s1.age)
print(s2.name,s2.name)
print(s1,s2)         #是同一个内存空间
输出:
埃利亚 20
埃利亚 埃利亚
<__main__.Single object at 0x000001935FC4CF28> <__main__.Single object at 0x000001935FC4CF28>
View Code

总结:所有的双下方法,没有需要在外部直接调用的;而是通过一些其他的内置函数,特殊的语法,来自动的触发这些双下方法

相关推荐