魔力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')
原文地址:https://www.cnblogs.com/smithpath/p/10614764.html