zoukankan      html  css  js  c++  java
  • 面向对象基础、继承、反射

    一  面向对象基础

    在 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会自动传递

     
  • 相关阅读:
    异步编程案例分享—后台线程事实通知任务进度
    ORACLE 常见错误
    设计模式——工厂模式
    sql 脚本编写之路 常用语句(一)
    C# 深入浅出 异步(八)
    设计模式——单例模式
    Sql Server 学习链接
    SqlServer 常用函数
    2016 ACM/ICPC Asia Regional Shenyang Online
    2016 ACM/ICPC Asia Regional Dalian Online
  • 原文地址:https://www.cnblogs.com/mushuiyishan/p/10471851.html
Copyright © 2011-2022 走看看