• 魔力Python——对象


      Python之中,一切皆对象.

       本文分为4部分:

      1. 面向对象:初识

      2. 面向对象:进阶

      3. 面向对象:三大特性----继承,多态,封装

      4. 面向对象:反射

    0. 楔子

    面向过程和面向对象的是两种思路:

    面向过程是流水线,闷着头一口气写下来就行;

    面向对象,感觉自己像是个上帝,先构思好,然后再去写,这也是我们写程序时候要做到的.

    例子如下:

    #面向过程,函数:
    def login():
        pass
    def regisgter():
        pass
    
    def func1():
        pass
    def func2():
        pass
    
    #面向对象,类:
    class Account:
        def login(self):
            pass
        def regisgter(self):
            pass
    
    class Account:
        def func1(self):
            pass
        def func2(self):
            pass

      从上面的式子,我们很容易发现:

        1.类相当于函数的集合,能够让你的函数更加集中,不是那么散乱.

        2.用类的话有上帝视角. 倒也算不上哪个好哪个坏,无非是复杂的话用类好,简单的话用函数好.

    1. 面向对象:初识

      1.1 什么是对象和类

      什么是对象?  人说,Python中,一切皆对象. 这么说似乎有些宽泛, 一切, 究竟包括什么呢?

      其实,这里的一切是指所有的变量. 在之前我们已经学习了字符串等数据类型,也学习了函数和内置函数,还学会了内置模块和第三方模块的调用. 那么,这些都是对象吗?是的,一切皆对象. 只要我们能够将其赋给变量,甚至连模块都可以赋给变量,那么还有什么不是变量呢?

      最近学到的一个经典例子:人狗大战. 这个例子可以生动形象的解释.

      如果我们需要进行人和狗进行战斗这样一个场景,我们需要的内容是:

      人的要素,比如生命值,攻击力,防御力,职业;狗的要素,生命值,攻击力,防御力,种类.

      创建完两个角色的属性后,我们需要让他们发生互动,也就是'fight'.

      但如果这时候,我们想要创建好几个人和狗怎么办呢? 很简单,我们只要把人和狗做成模子即可,就像活字印刷术,排完版之后蘸上墨水刷一下就好啦. 光想着这么多的东西,如果要是用函数的话,很容易没有条理的,尤其是创建人物和动物形象的时候.

      用函数做法如下:

    def Person(name,hp,ad,sex):   # 模子
        dic = {'name':name,'blood':hp,'attack':ad,'sex':sex}
    
        def fight(dog):  # 攻击  属于人
            # 狗掉血,就是人的攻击力
            dog['blood'] -= dic['attack']
            print('%s打了%s,%s掉了%s血' % (dic['name'], dog['name'], dog['name'], dic['attack']))
        dic['fight'] = fight
        return dic
    
    def Dog(name,hp,ad,kind):
        dic = {'name': name, 'blood': hp, 'attack': ad, 'kind': kind}
    
        def bite(person):  # 咬 属于狗
            person['blood'] -= dic['attack']
            print('%s咬了%s,%s掉了%s血' % (dic['name'], person['name'], person['name'], dic['attack']))
    
        dic['bite'] = bite
        return dic
    # 互相打
    smith = Person('smith',20,1,'不详')
    print(smith)
    hei = Dog('小黑',3000,10,'哈士奇')
    print(hei)
    smith['fight'](hei)
    hei['bite'](smith)

      说实话,是不是感觉乱乱的?  

      这时候,救星来了! 我们用类可以轻松做到!!!

    class Dog:
        def __init__(self,name,blood,ad,kind):
            self.name = name  # 向对象的内存空间中添加属性
            self.hp = blood   # 可以通过self在类的内部完成
            self.ad = ad
            self.kind = kind
    
        def bite(self,person):
            person.hp -= self.ad
            print('%s攻击了%s,%s掉了%s点血' % (self.name, person.name, person.name, self.ad))
    
    class Person:
        def __init__(self,name,hp,ad,sex):
            self.name = name
            self.hp = hp
            self.ad = ad
            self.sex = sex
    
        def fight(self,dog):
            dog.hp -= self.ad
            print('%s攻击了%s,%s掉了%s点血'%(self.name,dog.name,dog.name,self.ad))

      这样,两个模子已经刻好了,并且将动作也定义完了(此处就是方法),当我们想创建的时候,就可以轻松创建,如下:

    hei = Dog('小黑',300,20,'哈士奇')
    alex = Person('alex',20,1,'不详')
    alex.fight(hei)
    print(hei.hp)
    hei.bite(alex)
    print(alex.hp)

      这样,上面的就是创建类,下面的就是实例化对象并且进行方法的调用.(其中__init__是属性)

      类:具有相同方法和属性的一类事物

      对象:具有相同的属性值的实际存在的例子. 此处,对象仅指实例化的对象.是狭义的. 

        广义的对象包括类.

      1.2 类的结构和类的操作

      类的结构基本如下:

    class 类名:#切记,类名首字母大写,为了和系统内置的类区分开
        def __init__():#可有可无,不是必须写的
            pass
        def  方法名():
            pass

      查看类中变量的两种方法如下:

    def 函数:
        pass
    class Dog:
        pass
    class Dog:
        变量 = 1000
        变量2 = 'abc'
    #  变量3 = [(1,2),(3,4)]
    
    查看类当中的变量,方法一
    print(Dog.__dict__['变量'])
    print(Dog.__dict__['变量2'])
    print(Dog.__dict__['变量3'])
    查看方法二(常用)
    print(Dog.变量)
    print(Dog.变量2)
    print(Dog.变量3)

      其中,若用Dog.__dict__可查看Dog的全部属性,当然也可以用此方法查看实例化的对象等.

      调用类中静态变量的方法一致.

    #续上列:
    class Cat:
        cat_of_producing = '猫舍'#理解为刷怪区吧,是静态变量
        def __init__(self,name,hp):
            self.name = name
            self.hp = hp
        def catch(self):
            print('捕捉成功')        
    print(Cat.cat_of_producing)#此时就能调用静态变量
    #类名可以调用动态方法,但一般不用.
    #实例化对象也可以调用静态变量,能查能改.
    #但改的话一般用类名调用.

      非常需要注意:::::::::::::::::::::::::::::::::::::

     

      注意,当编译完了的时候,class就出现了其内存空间.当进行实例化的时候,其实例化对象也产生了一个内存空间.切记.

      

    实例化的过程 :
    1.开辟了一块空间,创造了一个self变量来指向这块空间
    2.调用init,自动传self参数,其他的参数都是按照顺序传进来的
    3.执行init
    4.将self自动返回给调用者

        PS:

      1.类名可以调用所有定义在类中的名字
      变量
      函数名
      2.对象名 可以调用所有定义在对象中的属性
      在init函数中和self相关的
      调用函数的,且调用函数的时候,会把当前的对象当做第一个参数传递给self

    2. 面向对象:进阶

      2.0 回顾

        回顾上述内容,记得设置完属性和动作之后让他们进行互动.

    class Monster:
        # 先生成一个怪物的形象
        def __init__(self, name, hp, attack, type):
            self.name = name
            self.hp = hp
            self.attack = attack
            self.type = type
    
        # 然后再生成一个怪物的动作
        def fight(self, person):
            person.hp -= self.attack
            print('%s对%s造成%s点伤害,%s还剩%s点血' % (self.name, person.name, self.attack, person.name, person.hp))
    
    
    class Hero:
        # 同理,先生成一个英雄的形象
        def __init__(self, name, hp, attack, sex):
            self.name = name
            self.hp = hp
            self.attack = attack
            self.type = type
    
        # 然后再生成一个怪物的动作
        def fight(self, monster):
            monster.hp -= self.attack
            print('%s对%s造成%s点伤害,%s还剩%s点血' % (self.name, monster.name, self.attack, monster.name, monster.hp))
    
    
    # 对人物和怪兽进行实例化
    arthur = Hero('King_Arthur', 50, 20, 'male')
    siren1 = Monster('海妖1号', 200, 10, 'siren')
    #经过千辛万苦,我们的亚瑟王最终和海妖战斗在一起,并且各砍一刀
    arthur.fight(siren1)#King_Arthur对海妖1号造成20点伤害,海妖1号还剩180点血
    siren1.fight(arthur)#海妖1号对King_Arthur造成10点伤害,King_Arthur还剩40点血
    #天呐,看来我们的亚瑟王如果不逃走的话将会命丧海妖之手,但是,,,,,,这跟我有什么关系呢哈哈哈哈哈
    
    #为了增加游戏的趣味性,我们可以再加上武器系统,比如再建一个class Weapon,然后通过动作进行武器装备,还可以加上防御力和
    # 闪避等属性,可操作性太多了.

        为了纪念这场差点把亚瑟王杀死的旷世大战,我准备把这个小游戏命名为世纪之战.

        记得,只要上面的世纪之战吃透,简单的写一个小小的自娱自乐的游戏至少是没问题哒.

      2.1 内存空间

      2.2 私有化(正常来说应当放到封装,但因为一些特殊原因放于此)

      2.3 类的特殊成员(前面有两道下划线的内置方法)

      

      2.1 内存空间

        内存空间:一般是指主存储器空间(物理地址空间)或系统为一个用户程序分配内存空间。

        内存地址:内存地址是内存当中存储数据的一个标识,并不是数据本身,通过内存地址可以找到内存当中存储的数据。由4位或8位16进制表示.

      2.2 私有化

        在前面加__即可,表明只有这个方法是由某类私有的.别的是调用不了的.

      2.3 类的特殊成员

        用到的时候可参考:https://www.jianshu.com/p/bf3166d8faf0

                 https://www.cnblogs.com/pyxiaomangshe/p/7927540.html

        之前的方法已经能够解决日常的90%了,我们需要解决的10%可以从内置方法中找到答案.

        内置方法又被称为双下方法,魔术方法.都是python的对象内部自带的,并且都不需要我们自己去调用它.

        2.3.1 __str__和__repr__

    class Course:
        def __init__(self,name,price,period):
            self.name = name
            self.price = price
            self.period = period
        # def __str__(self):#
        #     """
        #     打印这个对象的时候自动触发__str__
        #     :return:
        #     """
        #     return '%s,%s,%s'%(self.name,self.price,self.period)
        # def __repr__(self):#备胎(str没有的时候才用repr)
        #     """
        #     打印这个对象的时候自动触发__str__
        #     :return:
        #     """
        #     return '%s,%s,%s'%(self.name,self.price,self.period)
    
    python = Course('python',25000,'6 mouths')
    print(type(python))
    # print('course:%s'%python)
    # print(f'course:{python}')

        如果str存在,repr也存在,那么print(obj)和使用字符串格式化format,%s这两种方式 调用的都是__str__,而repr(obj)和%r格式化字符串,都会调用__repr__ ;
        如果str不存在,repr存在,那么print(obj),字符串格式化format,%s,%r 和repr(obj)都调用__repr__.
        如果str存在,repr不存在,那么print(obj)和使用字符串格式化format,%s这两种方式 调用的都是__str__.
        repr(obj)和%r格式化字符串,都会打印出内存地址.

    class Course:
        def __init__(self,name,price,period):
            self.name = name
            self.price = price
            self.period = period
        def __repr__(self):#备胎(str没有的时候才用repr)
            """
            打印这个对象的时候自动触发__str__
            :return:
            """
            return '%s,%s,%s'%(self.name,self.price,self.period)
        # def __str__(self):
        #     return self.name
    
    class Python(Course):
        pass
        def __repr__(self):
            return '%s--%s--%s' % (self.name, self.price, self.period)
        # def __str__(self):
        #     return '全栈开发:'+self.name
    
    python = Python('python',25000,'6mouths')
    print(python)
    #放开Courese和Python的str,显示为:全栈开发:python
    #放开Courese的str和Python的repr,显示为:python
    #放开Courese的repr和Python的str,显示为:全栈开发:python
    #放开Courese和Python的repr,显示为:python--25000--6mouths
        有了repr或者str在打印对象的时候 就不会显示用户不关心的内存地址了.
        增强了用户的体验 在程序开发的过程中,如果我们需要频繁打印对象中的属性,需要从类的外部做复杂的拼接,
      实际上是一种麻烦.

        如果这个拼接工作在类的内部已经完成了,打印对象的时候直接就能显示.

        2.3.2 __new__和__del__

        __new__ 构造方法 生产对象的时候用的 - 单例模式
        __del__ 析构方法 在删除一个对象之前用的 - 归还操作系统资源

        实例化一个Foo的对象,先开辟一块儿空间,使用的是Foo这个类内部的__new__.
        如果我们的Foo类中是没有__new__方法的,调用object类的__new__方法了

        在使用self之前,都还有一个生产self的过程,就是在内存中开辟一块属于这个对象的空间,并且在这个空间中存放一个类指针,以上就是__new__做的所有事情.

    #__new__的实例:
    class Foo(object):
        def __new__(cls, *args, **kwargs): # cls永远不能使self参数,因为self在之后才被创建
            obj = object.__new__(cls)   # self是在这里被创造出来的
            print('new : ',obj)
            return obj
        def __init__(self):
            print('init',self)
    Foo()
    #关于__del__的一些解释
    #在所有的代码都执行完毕之后,所有的值都会被python解释器回收

      #python解释器清理内存

      #1.我们主动删除 del obj
      #2.python解释器周期性删除
      #3.在程序结束之前 所有的内容都需要清空

     

    #
    __del__的实例1: import time class A: def __init__(self,name,age): self.name = name self.age = age def __del__(self): #只和del obj语法有关系,在执行del obj之前会来执行一下__del__中的内容 print('执行我了') a = A('gh',84) # print(a.name) # print(a.age) # del a print(a)#执行完后自动清理
    #__del__的实例2:
    import time
    class A:
        def __init__(self,path):
            self.f = open(path,'w')
        def __del__(self):
            '''归还一些操作系统的资源的时候使用'''
            '''包括文件网络数据库连接'''
            self.f.close()
    
    a = A('userinfo')
    time.sleep(1)

        PS:拓展:单例模式

          设计模式 - 单例模式
          一个类 有且只能有一个实例

          保证一个类无论 被实例化多少次,只开辟一次空间,始终使用的是同一块内存地址

    class A:pass
    a1 = A()
    a2 = A()
    print(a1)
    print(a2)
    
    class A:
        __flag = None
        def __new__(cls, *args, **kwargs):
            if cls.__flag is None:
                cls.__flag = object.__new__(cls)
            return cls.__flag
    
        def __init__(self,name=None,age=None):
            self.name = name
            if age:
                self.age = age
    
    a1 = A('alex',84)
    print(a1)
    a2 = A('alex',83)
    print(a2)
    a3 = A('alex')
    print(a3)
    print(a1.age)#结果为83,说明最后一次覆盖掉了

        2.3.3 __call__

    #__call__
    #源码里用比较多 Flask web框架
    #对象()自动触发__call__中的内容
    class A:
        def call(self):
            print('in call')
        def __call__(self, *args, **kwargs):#自动调用__call__
            print('in __call__')
    
    # A()()
    obj = A()
    obj()
    obj.call()
    转载自:https://segmentfault.com/q/1010000006113393?_ea=1020343(好吧,表示现在看的还不是太懂)
    用一句话描述是, Python 的
    __call__ 方法可以让类的实例具有类似于函数的行为,通常用于定义和初始化一些带有上下文的函数。 既然说是妙处,那不得不提及 Python 世界中的那几个经典实现了。 一个例子来源于 bottle 框架源码的 cached_property(被我改动了一些细节,但用法基本是一样的),为了在逻辑上构成一个封闭的整体,我们把一个实例当作函数来使用: class CachedProperty(object): _cache = {} def __call__(self, fn): @property @functools.wraps(fn) def wrapped(self): k = '%s:%s' % (id(self), fn.__name__) v = CachedProperty._cache.get(k) if v is None: v = fn(self) CachedProperty._cache[k] = v return v return wrapped cached_property = CachedProperty() 使用的时候可以直接替换掉内置的 property 来缓存动态属性: class DB(object): def __init__(self, ...): .... @cached_property def conn(self): return create_connection(...) db = DB() db.conn # 创建连接并缓存 还有个更复杂但非常实用的例子,Pipline,把函数封装成支持管道操作的运算过程: class Pipe(object): def __init__(self, fn): self.fn = fn def __ror__(self, other): return self.fn(other) def __call__(self, *args, **kwargs): op = Pipe(lambda x: self.fn(x, *args, **kwargs)) return op 可以像这样调用: @Pipe def sort(data, cmp=None, key=None, reverse=False): return sorted(data, cmp=cmp, key=None, reverse=reverse) [{'score': 1}, {'score': 3}, {'score': 2}] | sort(key=lambda x: x['score']) 可以看出,类 Pipe 被当作一个装饰器使用,所以 sort 函数的原始定义被传递给 Pipe.__init__,构造出一个 Pipe 实例,所以被装饰过的 sort 函数,也就是我们后面使用的那个,实际上是一个 Pipe 类的实例,只是因为它有 __call__ 方法,所以可以作为函数来使用。 这种用法在写一些 ORM 框架以及有大量细粒度行为的库时有奇效。

        2.3.4 __enter__,__exit__与with的完美结合

        自动识别__enter__和__exit__
        判定__enter__执行为开始,__exit__为结束
        应用场景应当有很多:比如

    执行语句体:
    class File:
        def __enter__(self):
            print('start')
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('exit')
    
    with File():
        print('wahaha')#相当于装饰器,输出顺序是
                                #start
                                #wahaha
                                #exit
    写入文件:
    class myopen:
        def __init__(self,path,mode = 'r'):
            self.path = path
            self.mode = mode
        def __enter__(self):
            print('start')
            self.f = open(self.path,mode=self.mode)
            return self.f
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
            print('exit')
    
    with myopen('userinfo','a')as f:
        f.write('hello,world')
    用pickle模块写入文件:
    import pickle
    class MypickleDump:
        def __init__(self,path,mode='ab'):
            self.path = path
            self.mode = mode
        def __enter__(self):
            self.f = open(self.path,self.mode)
            return self
        def dump(self,obj):
            pickle.dump(obj,self.f)
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
    with MypickleDump('pickle_file')as pickle_obj:
        pickle_obj.dump({1,2,3})

        第三种写入文件,请比较异同:

    class MypickleDump:
        def __init__(self,path,mode = 'ab'):
            self.path = path
            self.mode = mode
    
        def __enter__(self):
            self.f = open(self.path,self.mode)
            return self
    
        def dump(self,obj):
            pickle.dump(obj,self.f)
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
    with MypickleDump('pickle_file') as obj:
        obj.dump({1,2,3,4})
    
    with MypickelLoad('pickle_file') as obj:
       for i in  obj.loaditer():
           print(i)

        在一个函数的前后添加功能
        利用使用 装饰器函数中的内容

        with语句 就是和 __enter__,__exit__完美结合,请注意使用哦.

         

        ############################

          划    重    点

        ############################

        基础:
         __new__
         __del__
         __call__
        重点:
         __str__
         __repr__
         __enter__
         __exit__

    3. 面向对象:继承,多态,封装

      初入对象,感觉的确挺有意思,凡事都是那么简单的感觉,上帝视角,随意调用人物属性,随意设置任务动作,那滋味简直不要太爽!!!

      然而,记得一个公式:痛苦==有益,爽==有害.一旦只沉迷于这种感觉,很容易不思进取,因为再往下就会感觉有些难度了.

    图中虚线代表实例关系,实线表示继承关系,从这个图中得出几点:

    1. list、str、dict、tuple、type都继承了object,所以object是最顶层的基类

    2. type是本身的对象(实例),object、list、str、dict、tuple都是type的对象,所以type创建了所有的对象

    3. 综合1、2可知,一切皆对象的同时又继承了object类,这就是python的灵活之处,Python的面向对象更加彻底.

      其实,object也是继承元类(metaclass)得来的.

    PS:(转载自王sir)
    关于抽象类的理解:
    1.抽象类的意义是?或者说为什么要有抽象类? 抽象类是对多个类中共同方法的抽取,但是子类又有不同的实现,父类只能抽取出方法的名字,而不明确方法的具体实现. 这种只规定子类拥有哪些方法,而不明确具体实现的父类,就应该定义为抽象类. 抽象类只用来规范子类应该具有哪些行为,而不明确具体的动作. 2.抽象类的特点是? 抽象类和普通类的最主要区别在于: 1.抽象类继承的()中写的是metaclass=ABCMeta 2.在类体中有用@abstractmethod修饰的方法(需要子类重写的方法) 3.抽象类中是否可以有非抽象方法? 可以. 4.抽象类中的抽象方法是否可以有方法体? 可以. 5.抽象类中是否可以有__init__方法?为什么? 可以,因为抽象类中也可以有实例属性供子类继承. 而实例属性必须在父类中初始化,子类对象才能继承. 抽象类的__init__方法不能手动调用,只能是子类创建对象时自动调用的.

    4. 面向对象:反射

      首先,要明确一点的是,反射的格式:a.b <====> getattr(a,'b') 诸如此类,这是核心概念!!!!!

        所有的a,b都可以变成getattr(a,'b')
        用字符串数据类型的变量名 来获取实际的变量值
        用字符串数据类型的变量名 找到这个变量对应的内存地址

      4.1 两个内置函数

        issubclass

    # class A:pass
    # class B(A):pass
    # class C(A):pass
    # print(issubclass(B,A))
    # print(issubclass(C,A))
    # print(issubclass(B,C))#只针对类
    # print(isinstance(B,A))#只针对对象与类

        isinstance

    # a = 1
    # ret1 = type(a) is int
    # print(ret1)
    # ret2 = isinstance(a,int)
    # ret3 = isinstance(a,object)
    # print(ret2)
    # print(ret3)

        小结:

        两个内置函数
          isinstance()判断对象与类是否有继承关系
          type()判断对象与类是否有关系(只承认实例化对象的那个类,不承认继承关系)
          issubclass()判断类与类是否有继承关系

      4.2 一个套餐,4个函数

        内置函数:
        getattr
        hasattr
        setattr
        delattr

        4.2.1 hasattr()

          用途:hasattr#判断是否找到对象,判断True 或者 False

            格式:hasattr(a,'b'),返回True或者False. 常与getattr()连用,判断是否可以调用,避免报错.

        4.2.2 getattr()

          用途:getattr#找到对象对应的内存地址,调用字符串作为属性名

          格式:getattr(a,'b'),等同于a.b 

    if hasattr(s,'choose_goods'):   # 判断s对象有没有choose_goods
        func = getattr(s,'choose_goods')   # 使用s找到choose_goods对应的内存地址
        print(func)
        func()

            PS:若想调用变量a或b,但只能输入'a'或'b'.

    a = 1
    b = 2
    getattr(modules[__name__],'变量名')#这个一定要牢记!!!

        4.2.3 setattr()

          用途:用于添加,有三个变量.

          格式:setattr(x, 'y', v) is equivalent to ``x.y = v''#官方解释

    setattr(alex,'wahaha',wahaha)
    print(alex.__dict__)
    alex.wahaha(alex)

         4.2.4 delattr()

          用途:用于删除

          格式:delattr(x, 'y') is equivalent to ``del x.y''#官方解释

    delattr()
    print(smith.__dict__)
    delattr(smith,'name')   # del smith.name
    print(smith.__dict__)

      4.3 四种反射情况

        4.3.1 使用对象反射

          obj.属性名

          obj.方法名()

    # 1.使用对象反射(先列出类)
    # class Manager:   # 管理员用户
    #     def __init__(self,name):
    #         self.name  = name
    #     def create_course(self):  # 创建课程
    #         print('in Manager create_course')
    #
    #     def create_student(self): # 给学生创建账号
    #         print('in Manager create_student')
    #
    #     def show_courses(self): # 查看所有课程
    #         print('in Manager show_courses')
    #
    #     def show_students(self): # 查看所有学生
    #         print('in Manager show_students')
    # 不用反射
    # alex = Manager('alex')
    # operate_lst = ['创建课程','创建学生账号','查看所有课程','查看所有学生']
    # for index,opt in enumerate(operate_lst,1):
    #     print(index,opt)
    # num = input('请输入您要做的操作 :')
    # if num.isdigit():
    #     num = int(num)
    # if num == 1:
    #     alex.create_course()
    # elif num == 2:
    #     alex.create_student()
    # elif num == 3:
    #     alex.show_courses()
    # elif num == 4:
    #     alex.show_students()
    # 使用反射
    # alex = Manager('alex')
    # operate_lst = [('创建课程','create_course'),('创建学生账号','create_student'),
    #                ('查看所有课程','show_courses'),('查看所有学生','show_students')]
    # for index,opt in enumerate(operate_lst,1):
    #     print(index,opt[0])
    # num = input('请输入您要做的操作 :')
    # if num.isdigit():
    #     num = int(num)
    #     if hasattr(alex,operate_lst[num-1][1]):
    #         getattr(alex,operate_lst[num-1][1])()

        4.3.2 使用模块反射   

    import time
    time.time()
    
    调用函数和方法 地址+()
    def func():
        print(1)
    
    func()
    a = func
    a()
    # 反射模块中的方法
    import re
    # ret = re.findall('d+','2985urowhn0857023u9t4')
    # print(ret)
    # 'findall'
    # getattr(re,'findall')   # re.findall
    # ret = getattr(re,'findall')('d','wuhfa0y80aujeiagu')
    # print(ret)
    
    # def func(a,b):
    #     return a+b
    
    # wahaha = func
    # ret = wahaha(1,2)
    # print(ret)

        4.3.3 使用类反射

          cls.静态变量名
          cls.类方法名()
          cls.静态方法名()

    # 两种方式
        # 对象名.属性名 / 对象名.方法名() 可以直接使用对象的方法和属性
        # 当我们只有字符串数据类型的内容的时候
            # getattr(对象名,'方法名')()
            # getattr(对象名,'属性名')

        4.3.4 反射当前文件

    # 反射本文件中的内容 :只要是出现在全局变量中的名字都可以通过getattr(modules[__name__],字符串数据类型的名字)
    from sys import modules
    # print(modules)    # DICT KEY是模块的名字,value就是这个模块对应的文件地址
    
    # import re
    # print(re)   # <module 're' from 'D:\python3\lib\re.py'>
    # print(modules['re'])
    # 选课系统
    # 're': <module 're' from 'D:\python3\lib\re.py'>
    # print(re.findall)
    # print(modules['re'].findall)
    
    # a = 1
    # b = 2
    # while True:
    #     name = input('变量名 :')
    #     print(__name__)
    #     print(getattr(modules[__name__],name))
    
    # '__main__': <module '__main__' from 'D:/PyCharmProject/s20/day23/5.反射.py'>
    # print(a,b)
    # print(modules['__main__'].a)
    # print(modules['__main__'].b)
    #
    # print(getattr(modules['__main__'], 'a'))
    # print(getattr(modules['__main__'], 'b'))

        4.3.5 总结

    # hasattr和getattr
        # 只要是a.b这种结构,都可以使用反射
        # 用对象类模块反射,都只有以下场景
        # 这种结构有两种场景
        #     a.b   b是属性或者变量值
        #         getattr(a,'b')   == a.b
        #     a.b()  b是函数或者方法
        #         a.b()
        #             getattr(a,'b')()
        #         a.b(arg1,arg2)
        #             getattr(a,'b')(arg1,arg2)
        #         a.b(*args,**kwargs)
        #             getattr(a,'b')(*args,**kwargs)
        # 如果是本文件中的内容,不符合a.b这种结构
            # 直接调用func()
                # getattr(sys.modules[__name__],'func')()
            # 直接使用类名 Person()
                # getattr(sys.modules[__name__],'Person')()
            # 直接使用变量名 print(a)
                # getattr(sys.modules[__name__],'a')
        # 所有的getattr都应该和hasattr一起使用
            # if hasattr():
                 getattr()
    # setattr 只用来修改或者添加属性变量,不能用来处理函数或者是其他方法
        # a.b = value
        # setattr(a,'b',value)
        
    # delattr 只用来删除 属性变量
        # del a.b 删除属性  相当于删除了a对象当中的b属性
        # delattr(a,'b')
  • 相关阅读:
    flutter Sliver滑动视图组件
    Ionic4.x、Cordova Android 检测应用版本号、服务器下载文件以及实现App自动升级、安装
    flutter SnackBar 底部消息提示
    Flutter ExpansionPanel 可展开的收缩控件
    Ionic4 Cordova 调用原生硬件 Api 实现扫码功能
    Flutter BottomSheet底部弹窗效果
    Flutter 中AlertDialog确认提示弹窗
    Ionic Cordova 调用原生 Api 实现拍照上传 图片到服务器功能
    Flutter 中SimpleDialog简单弹窗使用
    Springboot项目mysql日期存储不匹配问题和在idea本地可以运行起来,但打包jar后运行报找不到mysql驱动的解决方案
  • 原文地址:https://www.cnblogs.com/smithpath/p/10614764.html
Copyright © 2020-2023  润新知