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

     
  • 相关阅读:
    springmvc
    POJ 3683 Priest John's Busiest Day
    POJ 3678 Katu Puzzle
    HDU 1815 Building roads
    CDOJ UESTC 1220 The Battle of Guandu
    HDU 3715 Go Deeper
    HDU 3622 Bomb Game
    POJ 3207 Ikki's Story IV
    POJ 3648 Wedding
    HDU 1814 Peaceful Commission
  • 原文地址:https://www.cnblogs.com/mushuiyishan/p/10471851.html
Copyright © 2011-2022 走看看