面向对象基础、继承、反射

一  面向对象基础

在 Python 中,面向对象编程主要有两个主题,就是类和实例。

类大致由类变量,实例变量,类方法三部分组成

class Human:  # class是关键字,Human是类名,类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。
    """
    此类主要是构建人类      #解释文档
    """

mind = '有思想' # 类变量 faith = '佛系'
def __init__(self, name, age): # 实例变量 self.name = name self.age = age
def work(self): # 方法 、 动态属性 #类方法 print('%s的工作' % self.name)
p1
= Human('matt',20) #调用

类的特点:

  • 多态:对不同的类对象使用相同的操作方法。
  • 封装:封装之后,可以直接调用类的对象,来操作内部的一些类方法,不需要让使用者看到代码工作的细节。
  • 继承:类可以从其它类或者元类中继承它们的方法,直接使用。

实例化时共发生了三件事:

  • 在内存中开辟了一个空间; ===》__new__方法,构造函数
  • 将空间传给__init__(self)中的self,并执行类中的__init__方法;===》初始化函数
  • 将空间返回给调用的实例。

封装的定义:

  • 广义:把变量和方法封装在一个类里,定义一个规范来描述一类事物
  • 狭义:私有化,只能在类的内部访问

1. 类变量操作

1.1 __dict__  查看所有类变量和实例变量

class Human:
    faith = '佛系'
    food = 'meat'
    hair = 'black'
    def __init__(self, name,age,hobby):      #构造函数
        self.name = name
        self.age = age
        self.hobby = hobby
    def work(self):
        print('%s的工作'%self.name)
print(Human.__dict__ )
p = Human('matt',20, 'dog')
print(p.__dict__)


{'__module__': '__main__', 'faith': '佛系', 'food': 'meat', 'hair': 'black', 
'__init__': <function Human.__init__ at 0x00000000025CA9D8>, 'work': <function Human.work at 0x00000000025CAE18>,
'__dict__': <attribute '__dict__' of 'Human' objects>,
'__weakref__': <attribute '__weakref__' of 'Human' objects>, '__doc__': None} #所有的类变量
{
'name': 'matt', 'age': 20, 'hobby': 'dog'} #所有的实例变量

注意:实例空间仅仅存放实例变量,不会存放类变量和类方法;通过实例访问类变量和类方法时,实例会根据构造函数声明位置向上查找。

1.2 外部对类变量的增删改查

class Human:
    faith = '佛系'
    food = 'meat'
    hair = 'black'
    def __init__(self, name,age,hobby):
        self.name = name
        self.age = age
        self.hobby = hobby
    def work(self):
        print('%s的工作'%self.name)
Human.eye = 'blue'         #增
del Human.hair             #删
Human.food = 'rice'        #改
print(Human.faith)         #查,查找单个变量
print(Human.__dict__)      #查看所有变量

佛系      
{'__module__': '__main__', 'faith': '佛系', 'food': 'rice', '__init__': <function Human.__init__ at 0x00000000027AA9D8>, 
'work': <function Human.work at 0x00000000027AAE18>, '__dict__': <attribute '__dict__' of 'Human' objects>,
'__weakref__': <attribute '__weakref__' of 'Human' objects>, '__doc__': None, 'eye': 'blue'}

1.3 内部函数对类变量的增删改查

#自身内部函数对类变量的修改
class
Human: faith = '佛系' food = 'meat' hair = 'black' def __init__(self, name): self.name = name def work(self): Human.eye = 'blue' del Human.hair Human.faith = 'God' print(Human.food) #访问类变量只能通过定向查找(Human.food)的方式,即使在类内部的函数也不能用food,可以认为,只有定向查找才开启向上查找,否则仅仅本空间查找。 对比2.2 Human.work('matt') print(Human.__dict__)
# 2号类中的函数对1号类中类变量的修改
class Human2: def work(self): Human.faith = 'God2' Human2.work('matt') #类名 调用 类方法 print(Human.faith)

meat
{'faith': 'God', 'food': 'meat', '__init__': <function Human.__init__ at 0x00000000027AABF8>, 'work': <function Human.work at 0x00000000027AAE18>, 'eye': 'blue'}
God2

总结:只要有类名这一指针,就能完成对类内部变量的操作

2. 实例变量操作

2.1 外部对实例变量的增删改查

class Human:
    faith = '佛系'
    food = 'meat'
    hair = 'black'
    def __init__(self, name,age,hobby):
        self.name = name
        self.age = age
        self.hobby = hobby
    def work(self):
        print('%s的工作'%self.name)
p = Human('matt',20,'dog')
p.eye = 'blue'       #增
del p.hobby          #删
p.age = 30           #改
print(p.name)        #查
print(p.__dict__)

matt
{'name': 'matt', 'age': 30, 'eye': 'blue'}

2.2  内部函数对实例变量的增删改查

#自身内部函数对实例变量的操作
class
Human: def __init__(self, name,age,hobby): self.name = name self.age = age self.hobby = hobby def work(self): self.eye = 'blue' del self.hobby self.age = 30 print(self.name) # 对比 1.3 p = Human('matt',20,'dog') p.work() print(p.__dict__)
#2号类中函数对1号类实例变量的操作
class Human2: def work(self): p.name = 'logan' p2 = Human2() p2.work() print(p.__dict__)

matt
{'name': 'matt', 'age': 30, 'eye': 'blue'}
{'name': 'logan', 'age': 30, 'eye': 'blue'}

只要有实例名称,就能修改实例变量,同类变量一致

3. 变量的命名空间解析

 Human是类名也是命名空间名称,P是实例名称也是命名空间名称;形如Human. faith 和 p. name,可完成定向操作

类变量和实例变量的本质都是字典,字典在参数传递中属于可变数据类型。

 

class Human:
    faith = '佛系'
    print(faith)              #id = 1 
    def __init__(self, name):
        self.name_obj = name
        self.eye = 'blue'
        age = 20
        print(locals())
    def work(self):
        print(self.name_obj)
    print(locals())        # id = 2 
p = Human('matt')          # id = 3 
print(p.__dict__)          # id = 4
p.work()                   # id = 5
print(globals())           # id = 6

佛系   #id = 1 
{'__qualname__': 'Human', 'faith': '佛系', '__init__': <function Human.__init__ at 0x000000000248A9D8>, 'work': <function Human.work at 0x000000000248AE18>}
# id = 2 类Human中的变量 {
'self': <__main__.Human object at 0x00000000020CC748>, 'name': 'matt', 'age': 20}
# id = 3 实例化时只会执行构造函数__init__,此变量空间里未生成name_obj 和 eye ,因为两者在定向操作的实例p空间下生成。 {
'name_obj': 'matt', 'eye': 'blue'}
#id = 4 执行构造函数后,在实例p空间下生成的结果,只会生成带self的变量。 matt #id = 5 通过实例名调用类函数,在实例空间找不到work变量后,会根据构造函数的声明位置进行向上查询,直到找到或报错。但仅限于查询,不能修改,类似于嵌套函数中,内层对外层只能访问,不能修改。 {
'__name__': '__main__', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000001FB1CF8>,
'__file__': 'E:/2019allstark/practice1/1/001.py', 'Human': <class '__main__.Human'>, 'p': <__main__.Human object at 0x00000000020CC748>}
# id = 6 全局变量, 虽然类Human和实例p的命名空间是同级的,但实例p可以通过构造函数的声明位置向上寻值,因此可以通过实例访问类变量和类函数

3.1 属性查找的顺序

类向上查找的方式,此方式不同于嵌套函数的向上查找。

  • 实例查找属性的顺序:先从对象空间找  ------> 类空间找 ------> 父类空间找 ------->.....     #类空间找不到去父类里找,而不是到全局变量查找
  • 类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> ........

上面的顺序都是单向不可逆,类名不可能找到对象的属性,并且只是查询,不能修改。类似于嵌套函数,内层函数只能对外层中的变量进行访问,不能修改,修改就是在内层创建新值。

def func():
    i = 1
    def func1():
        # del i      #取消注释会报错,不能修改外部命名空间的变量。
        print(i)
    func1()
func()

1

3.2 函数对全局变量(可变类型)的修改

类变量和实例变量的本质是字典,函数对全局变量(可变类型)的修改  与  类变量和实例变量的修改  一致,都属于定向操作

dict1 = {'a': 1, 'b': 2}
con1 = 3
def func(var):
    var = 4          #在函数内部生成一个新变量,不可变数据不会被修改,只会生成新值
def func1(var):
    dict1['a']=var   #定向修改可变数据
func(10)
func1(10)
print(dict1)
print(con1)

 3.3  练习

1、计算一个类实例化的次数

class H:
    count = 0
    def __init__(self):
        H.count +=1                  #只有定向查找才能开启向上查找,并且不会查找全局变量
        # H.count = self.count +1    #与上一句作用相同
        print(H.count)
h1 = H();h1 = H();h1 = H();h1 = H();h1 = H()

1;2;3;4;5

2、游戏练习

class GameRole:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp
    def attcak(self, p, w):
        p.hp -= w.ad
        print('%s 使用 %s 攻击 %s , 掉了%s血, 还剩%s' % (self.name, w.name, p.name, w.ad, p.hp))
class Weapon:
    def __init__(self, name, ad):
        self.name = name
        self.ad = ad
p1 = GameRole('matt', 500)
p2 = GameRole('logan', 300)
w1 = Weapon('axe', 40)
w2 = Weapon('sword', 60)
p1.attcak(p2, w1)

matt 使用 axe 攻击 logan , 掉了40血, 还剩260

二  继承

类继承的本质是:为  实例变量  、类变量方法 或者  构造函数  提供向上查找的命名空间。类继承不会复制任何内容,仅仅拓宽了向上查找的命名空间。

类向上查找的方式:

  • 实例查找属性的顺序:先从对象空间找  ------> 类空间找 ------> 父类空间找 ------->.....
  • 类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> ........

1. 单继承

1.1 构造函数的继承

声明的类中不包含构造函数时会向上查找。

class Human:
    def __init__(self,name):
        self.name = name
    def func(self):
        print('1')

class People(Human):   #子类执行父类的构造函数,但寻值时还是从子类开始,区别于从构造函数声明位置开始
    def func(self):
        print('2')
p = People('matt')
p.func()

2

1.2 同时执行本类和父类的构造函数

class Human:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def run(self):
        print('running...')
class People(Human):
    def __init__(self, name, age, hobby):
        # Human.__init__(self, name, age)             #一般不写这种,注意这是调用语句,不是声明语句    
        super().__init__(name, age)                   #推荐写法,super是严格按照继承顺序执行的
        # super(People, self).__init__(name, age)     #上式的简写
        self.hobby = hobby
    def jump(self):
        print('jumping...')
p = People('matt','20','dog')

1.3 同时执行父类和子类的同名方法

class Human:
    def run(self, var):
        print('1234%s'%var)
class People(Human):
    def run(self,var):    
        print('2234')
        super().run(var)       #super隐藏self参数,super按照继承顺序执行
p = People()
p.run('啊啊啊')

2234
1234啊啊啊

2. 多继承

新式类:继承object的类,广度优先,进入点有且仅有一个,且后来的进入

经典类:不继承object的类,深度优先,一条路走到黑

新式类查询继承顺序:__mro__, 算法:C3

class A:  pass
class B:  pass
class C(A, B):  pass
class D(A, B):  pass
class E(C, D):  pass
print(E.__mro__)  # __mro__继承顺序

(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

参考:C3算法详细解析

https://www.cnblogs.com/jin-xin/articles/10274734.html

三  多态

1. 定义

一种类型的多种表现形态

举例:类B和类C都继承类A,B和C就是A的两种表现形态,python中一切皆对象,多有的对象都继承object,所以python中处处是多态。

2.  抽象类

抽象类中只能有抽象方法,子类继承抽象类时,不能通过实例化使用其抽象方法,必须实现该方法。一句话,让继承的子类中必须定义该方法。

抽象类(单继承)接口类(多继承,约束为pass)是在java中区分的,在python中不区分

from abc import ABCMeta,abstractmethod    #abc模块就是关于抽象类模块
class Abs(metaclass=ABCMeta):             #抽象类
    @abstractmethod 
    def foo(self):                        #定义抽象方法,无需实现功能
        pass
class A(Abs):                             #子类继承抽象类,必须定义抽象方法
    def foo(self):
        print('aa')
class B(Abs):                             #子类继承抽象类,不定义抽象方法,实例化时会报错
    pass
a = A();a.foo()                 
# b = B()

aa

参考:抽象了概念解析

http://www.imooc.com/article/74245

3. 鸭子类型

规范全凭自觉

class A:
    def f1(self):
        print('in A f1')
    def f2(self):
        print('in A f2')
class B:
    def f1(self):
        print('in A f1')
    def f2(self):
        print('in A f2')
a = A();b = B()

# A 和 B两个类完全没有必然联系,但是在某种意义上他们却统一了一个标准。
# 对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。
# 这样的例子比比皆是:str  tuple list 都有 index方法,这就是统一了规范。
# str bytes 等等 这就是互称为鸭子类型。

四  类的其他成员

class A:

    class_name = 'class'      # 类变量
    __iphone = '1353333xxxx'  # 私有类变量:双下划綫开头,只有本类内部能够访问,定向访问无效,一句话:只有本类函数能调用

    def __init__(self,name,age):   # 构造方法
        self.name = name           # 实例变量
        self.__age = age           # 私有实例变量:双下划綫开开头,只有本类函数能调用

    def func1(self):               # 方法、实例方法,self代表实例
        pass
    def __func(self):              # 私有方法:双下划綫开头,
        print(666)

    @classmethod                   # 类方法(classmethod),cls代表实例的父类
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod                  # 静态方法
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')

    @property                      # 属性方法
    def prop(self):
        pass

1. 私有变量

双下划线开头,在类内部的方法中使用时 self.__private_attrs或者self.__private_methods

私有变量的本质就是只留一个访问接口,留给本类的方法。

私有化:在类的内部访问,你就知道了在哪个类中。

1.1 私有类变量

class A:
    __NAME = "matt"      #私有类变量一般都是全大写
    def func(self):
        print(A.__name)
class B(A):
    def show(self):
        print(A.__name)
# A.__NAME            #报错
obj = A()
# obj.__NAME          #报错
obj.func()     
obj_son = B()
# obj_son.show()      #报错
obj_son.func()
 matt; matt

1.2 私有实例变量

class C:
    def __init__(self):
        self.__foo = "matt"
    def func(self):
        print(self.__foo)
class D(C):
    def show(self):
        print(self.__foo)
obj = C()
# obj.__foo               #报错
obj.func()
obj_son = D()
# obj_son.show()          #报错;执行父类构造函数,但依旧会报错
obj_son.func()

matt;matt

1.3 私有方法

class C:
    def __init__(self):
        pass
    def __add(self):
        print('C')
class D(C):
    def __show(self):
        print('D')
    def func(self):
        self.__show()
obj = D()
# obj.__show()     #报错
obj.func()
# obj.__add()      #报错

D

2. 静态方法

@staticmethod   静态方法是类中的独立函数,不需要实例,不需要传递self参数。简单说:静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。

调用:实例名称+方法名称

class Human:
    def __init__(self,name):
        self.name = name
    @staticmethod
    def func(a, b ):
        return a+b
p = Human('matt')
print(p.func(1,2) )   #3

3. 类方法

普通的方法叫做实例方法,导入的self代表实例, 类方法(@classmethod)导入的第一个是类cls

调用方法:只能通过类名+方法名称,这种调用方法很少见

class Dog(object):
    food = "bone"
    age = "1"
    def __init__(self, name):
        self.name = name
    @classmethod
    def eat(cls,age):
        #print(self.name)   会报错,这是因为类变量里没有name
        print(age)
        print(cls.food)
d = Dog("金毛")
d.eat(Dog.age)         #1;bone

用途:一般是调用类方法去修改类变量

class ClassTest(object):
    __num = 0
    @classmethod
    def addNum(cls):
        cls.__num += 1
    @classmethod
    def getNum(cls):
        return cls.__num
    def __new__(self):
        ClassTest.addNum()
        return super(ClassTest, self).__new__(self)   # return object.__new__(self)等效
class Student(ClassTest):
    def __init__(self):
        self.name = ''
a = Student();b = Student();print(ClassTest.getNum())   #2

4. 属性方法

4.1 @ property  

函数不用‘’()‘’直接调用,一般都有返回值,形式上类似于变量,一句话:伪装成属性,函数调用时不用()

用途:返回的是一个属性,这个属性一般会随着类或者实例的基础数据变化而变化,例如求圆的面积

class Flight:
    def __init__(self, name):
        self.flight_name = name
    def checking_status(self):
        print("checking flight %s status " % self.flight_name)
        return 1
    @property
    def flight_status(self):
        status = self.checking_status()
        if status == 0:
            print("flight got canceled...")
        elif status == 1:
            print("flight is arrived...")
        else:
            print("cannot confirm the flight status...,please check later")
f = Flight("CA980")
f.flight_status         #不用()就可以调用,形式类似于变量

checking flight CA980 status
flight is arrived...

4.2 @method_name.setter;@method_name.deleter

class A:
    def __init__(self,name,age):
        if type(age) ==int:
            self.name = name
            self.__age = age
        else:
            print('age输入数字')
    @property                      # 属性化方法
    def age(self):
        return self.__age
    @age.setter                    # a.gae = '20' 语句调用,@name.setter执行完之后执行@property;
  
def age(self,var): # var 为赋值的数据;设置这个装饰器的原因在于a.age = ‘20’ ,age本质是个方法,若将其改为字符串等数据,会影响后期调用操作。 if type(var) == int: self.__age = var else: print('重新输入数字') @age.deleter # del a.age 语句调用,之后不会自动执行@property def age(self): del self.__age a = A('matt',23) a.age = '20' print(a.age) del a.age # print(a.age)

重新输入数字; 23 

5. 双下方法

双下方法一般都放置在类中,其调用方法不尽相同(内置函数调用,特殊符号如:+调用);别名:特殊方法,魔术方法,内置方法。

双下方法(类的内置方法)一般与内置函数有联系:内置函数一般都是调用双下方法来实现功能的(间接调用),双下的返回值就是内置函数的返回值。

1、__doc__  注释文档

2、__module__ 和  __class__ 

  __module__ 表示当前操作的对象在那个模块

  __class__     表示当前操作的对象的类是什么

3、 __init__  实例化方法

实例化时自动执行

4、__del__  析构方法

调用:对象在内存中被释放时,自动触发执行,进行垃圾回收,分为两种执行情况:

  • 主动这行del语句;
  • 在程序最后执行,因为所有程序执行在全部执行完之后都要消除变量,释放内存
class A:
    def __init__(self,name,age):
        self.name =name
        self.age =age
    def __del__(self):
        print('asd')
a = A('matt',20)
b = A('loagn',30)
del a.name            #删除实例变量不会触发
print('-----')
del a                 #触发:先执行print(‘asd’),再执行删除实例,因为__del__需要传入self的值。
print('-----')        #所有语句执行完之后,消除所有实例、变量,触发

-----;asd;-----;asd   

 用途:主要用于在最后关闭打开的文件;

class File:
    def __init__(self, file_path):
        self.f = open(file_path)
    def read(self):
        self.f.read()
    def __del__(self):           #保证最后一定能关闭文件
        self.f.close()          
file = File('file_path')
file.read()

5、 __call__  

调用:实例名称+()

__inin__的调用方式为:类名称+();__call__的调用方式为:实例名称+()

class A:
    def __call__(self, *args, **kwargs):
        print('call')
class B:
    def __init__(self,cls):
        self.cls =cls()
        self.cls()
    def func(self):
        pass
a = A(); a() #调用__call__的方式:实例名称+() b = B(A) #源代码中常用此方式

call;call

6、__dict__   查看类或实例中的所有成员  

不同于内置方法dict(),后者为用多种方法构造字典

class A:
    def __init__(self,name,age):
        self.name =name
        self.age = age
    def func(self):
        pass
a =A('matt',20)
print(A.__dict__);print(a.__dict__)

{'__module__': '__main__', '__init__': <function A.__init__ at 0x00000000027AABF8>, 'func': <function A.func at 0x00000000027AAE18>, 
'__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} {'name': 'matt', 'age': 20}

7、__str__ 

调用: print(obj. __str__),打印时自动执行,并非str()调用;

返回值必须是字符串格式

li = [1,2];print(li)       # print(li) 等效于print(list.__str__)

class A:pass a = A();print(a) # 等效于print(object.__str__),默认为内存空间
class B: def __init__(self,name ,age): self.name =name self.age =age def __str__(self): return '%s is %s'%(self.name,self.age) #返回值必须是字符串 b = B('matt',20) print(b) # 等效于print(b.__str__) [1, 2] <__main__.A object at 0x00000000020CC7B8> matt is 20

__str__并不是由str()函数调用的;str(obj)是内置类,其返回obj的字符串格式

li = [1,2,3]
class A:
    def __str__(self):
        pass
class B(A):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return  str(self.name)   #调用的是内置类
b =B(li)
print(b);print(type(b))

[1,2,3] ; <class '__main__.B'>

8、repr

调用:repr(obj) ; 作为__str__的备胎,类中不存在__str__时调用;%r

li = 'asd'print(repr(li))
class A: def __repr__(self): return 'asd' #返回值必须是字符串 a =A();print(a)

print('我是%r'%'matt')

'asd';asd; 我是'matt'          #repr() , %r 与__repr__略有不用

__str__查找顺序:先去父类,然后再__repr__

class A:
    def __str__(self):
        return 'A.__str__'
class B(A):
    def __repr__(self):
        return 'B.__repr__'
b =B();print(b)

A.__str__

9、__getitem__、__setitem__、__delitem__

对象通过 [ ] 访问、设置、删除值有关

class A(object):
    def __getitem__(self, key):
        print('__getitem__', key)
    def __setitem__(self, key, value):   #要求三个参数
        print('__setitem__', key, value)
    def __delitem__(self, key):
        print('__delitem__', key)
a = A()
result = a['k1']    #查询,触发,三个方法都需要[ ]触发
a['k2'] = 'alex'    #设置,触发
del a['k1']         #删除,触发

__getitem__ k1
__setitem__ k2 alex
__delitem__ k1

作用:在内置模块中,有一些特殊的方法,要求实例必须实现__getitem__/__setitem__ /__delitem__才能使用

10、__len__  返回对象内的元素个数

调用:len(obj)

# 解析内置函数len()与双下方法的联系
class
A: def __len__(self): return 100 a = A() print(len(a)) #内置函数都是调用双下方法来实现的,双下方法的返回值就是内置函数的返回值 print(a)     #内置函数len(a),处理过程为首先找到实例a的类,然后执行类中的__len__()方法,若本类中没有,则去父类中查找 100 <__main__.A object at 0x00000000020477F0>

11、__new__  

实例化时完成三件事:

  • 开辟一块内存空间;
  • 将空间传给__init__(self)中的self,并执行__init__方法;
  • 将空间传给调用者
class A:
    def __new__(cls, *args, **kwargs):     #传入的参数是类,因为此时实例还未生成,cls = A
        obj = object.__new__(cls)          #调用原类object中的__new__方法,实质为开辟内存空间
        print(obj)
        return obj                         #返回obj空间        
    def __init__(self):                    #接受__new__方法返回的空间,并赋值给self,之后执行__init__方法
        print(self)
a = A()                                    #执行顺序为:A() >>> __new__ >>> __init__ >>> a  = A()
                                           #self赋值给了a,而不是a赋值给了self
<__main__.A object at 0x00000000023F82B0>    
<__main__.A object at 0x00000000023F82B0>

单例类:一个类始终只能只有一个实例

class Single:
    __SPACE = 0
    def __new__(cls, *args, **kwargs):
        if cls.__SPACE == 0:
            cls.__SPACE = object.__new__(cls)
        return cls.__SPACE
    def __init__(self, name):
        self.name = name
a = Single('matt')
b = Single('logan')
print(a.name);print(b.name)

logan;logan

12、__eq__    与  == 一致

判断两个对象是否相等

def func():
    a = 10246
    return a
def func1():
    b = 10246
    return b
a = func();b = func1()
print(id(a));print(id(b))
print(a == b);print(a is b);print(a.__eq__(b))
print(set((a,b)))

33937392;33936816;True;False;True; {10246}   #验证与==一致

重构__eq__方法

class A:
    def __init__(self,name,age):
        self.name =name
        self.age = age
    def __eq__(self, other):
        if self.name == other.name and self.age == other.age:
            return True
a = A('matt',20);b = A('matt',20)
print(a == b)

True

13、__hash__

这个hash与文件的hash(hashlib)不相同

__hash__和__eq__经常是一起出现的

#自己修改的类似于__eq__和__hash__的源码,仅供理解,源代码并非如此
def
__hash__(self): return hash(id(self)) # hash的是实例的内存地址,能够解释同一数据在一次执行中hash值相同,在不同执行中不同
def __eq__(self, other):
if isinstance(other, self.__class__): #首先判读是否属于一类 return self.__dict__== other.__dict__ #判断只是否相等此行与参考不一致 else: return False

 set、frozenset和dict这些集合利用hash函数创建键,利用不可变对象的哈希值来高效查找集合中的对象。

须知:不同的内存地址可能hash值一样,;hash值不同,内存地址一定不同

set去重解析:

  • set() 函数中会先调用对象的 __hash__() 方法,获取 hash 结果;
  • 如果 hash 结果相同(桶球理论,hash值相同会自动提示),用比较操作符 == (也就是调用函数 __eq__())判断二者的值是否相等;
  • 如果都相等,去重;否则,set() 认为二者不同,两个都保留到结果中。

通过重构__hash__和__eq__方法来自定义去重

class Student:
    def __init__(self,name,age,department):
        self.name =name
        self.age =age
        self.depatment =department
    def __hash__(self):
     print('aaa')
return hash(self.name+str(self.age)) # hash是调用的str类中的,hash之后就存储数据(桶球理论) def __eq__(self, other): # 要存入的地址中已有数据,则调用__eq__方法      print('bbb')
if self.name == other.name and self.age == other.age: return True # 返回True表示相等,不存入 def __str__(self): #无效 return self.name s1 = Student('matt',25,'01') s2 = Student('matt',25,'02') s3 = Student('logan',25,'02') print(set((s1,s2,s3)))
aaa;aaa;bbb;aaa {
<__main__.Student object at 0x000000000244C7B8>, <__main__.Student object at 0x00000000024BA4A8>}

参考:

类似__eq__和__hash__原码解析

https://blog.csdn.net/anlian523/article/details/80910808

__eq__、__hash__与比较运算符解析

https://blog.csdn.net/q1403539144/article/details/94400722

set去重机理解析

https://blog.csdn.net/qq_41359051/article/details/91836100

五、反射

1. 反射基础

反射:通过字符串获取方法。

  • hasattr(object, name)     判断对象object是否包含名为name的特性。
  • getattr(object,name,default)    返回object的name属性值,若name不存在,则触发AttribetError异常或返回default值。
  • setattr(object,name,method)   setattr(x, 'y', v)  等效于 `` x.y = v ''   (不常用)
  • delattr(object,name)            删除name属性                                 (不常用)

object可以是实例、类、也可以是模块,其本质上就是一个命名空间,查找该命名空间下的name变量。

hasattr ()和 getattr() 都是一起用的

def bulk(self):
    print("%s is bulking。。。。。。"%self.name )
# bulk =12
class Dog(object):
    def __init__(self, name):
        self.name = name
    def eat(self, food):
        print("%s is eating......" % self.name, food)
d = Dog("金毛")
choice = input("请选择方法>>").strip()
if hasattr(d, choice):          # choice是字符串格式
    func = getattr(d, choice)  # 获取内存地址
    func("狗粮")
else:
    setattr(d, 'foo', bulk)    # d.foo = bulk
    # print(d.__dict__)
    d.foo(d)                   #调用函数有些问题,在于self传值问题,此处添加的方法类似于静态函数staticmethod,不常用

请选择方法>>32
金毛 is bulking。。。。。。

另外:

class Foo:
    def __init__(self):
        self.name = 'wupeiqi'
    def func(self):
        return 'func'
    @staticmethod                   
    def bar():
        return 'bar'
print(getattr(Foo, 'bar'))
# delattr(Foo, 'func')                  # 注意字符串形式
del Foo.func                            # 与上式等效,注意是变量形式
print(Foo.__dict__)                     # 静态方法也能被查找
f = Foo()
# delattr(Foo, 'func') ; def f.func     # 此两种方法都会报错,因为f.__dict__没有func方法

<function Foo.bar at 0x000000000279AD08>
{'__module__': '__main__', '__init__': <function Foo.__init__ at 0x000000000279A9D8>,

'bar': <staticmethod object at 0x000000000244C780>, '__dict__': <attribute '__dict__' of 'Foo' objects>}

2. 反射的应用

反射大致分为:对类反射,对实例反射,对本模块反射,对其他模块反射。

2.1 对本模块反射

import sys
def func1():
    print('aa')
def func2():
    print('bb')
# file = sys.modules[__name__]      # 与下式等效,注意__name__是变量,sys.moudles获取加载的所有模块
file = sys.modules['__main__']      # 获取本文件(模块)的名称(地址),类似os、sys等
print(file)
getattr(file, 'func1')()

<module '__main__' from 'E:/2019allstark/practice1/1/002.py'>
aa 

六  函数和方法

1. 打印名称确认

def func():
    pass
class A:
    def func1(self):
        pass
    @staticmethod          #实例的静态方法也是函数,不自动传递self       
    def func2(self):
        pass
a =A()
print(func)         
print(A.func1)            #一般不会采用 cls.function 的形式,因为这样不会自动传递self
print(a.func1)            #一般都是采用此种形式调用类中的方法,自动传递self
print(a.func2)             

<function func at 0x0000000001FDC1E0>
<function A.func1 at 0x000000000279AE18>
<bound method A.func1 of <__main__.A object at 0x000000000241C780>>
<function A.func2 at 0x000000000279AD08>    

2. types模块确认

from types import FunctionType
from types import MethodType
def func():
    pass
class A:
    def func1(self):
        pass
a = A()
print(isinstance(func,FunctionType))  
print(isinstance(A.func1,FunctionType))  
print(isinstance(a.func1,FunctionType)) 
print(isinstance(a.func1,MethodType))  

True;True;False;True

3.两者区别

两者的区别在于是否主动传递self,function不会自动传递self,而method会自动传递

 
原文地址:https://www.cnblogs.com/mushuiyishan/p/10471851.html