• python学习第20天----面向对象复习、__repr__


    1.面向对象

    #问题:

             ①写代码时,什么时候用面向对象:处理比较复杂的角色之间的关系(代码量大,功能多的时候),如复杂的电商程序、公司/学校的人事管理的系统;提高代码的可读性,清晰度(无论是开发者还是调用者,都能明确的分辨出每个角色拥有的方法和属性);增强代码的可扩展性;增加了复用性;代码更加规范

             ②如何理解Python中一切皆对象:每一个基础数据类型都是对象,有属于自己的类,每个类特有的方法,只能属于本类的对象去使用

      ③类型和自定义类的关系:类型和类是一个东西,自定义的每一个类都是一个新的数据类型;当type(obj)时,如果obj是一个对象,那么它的type就是它的类型

      ④创建一个类:class 类名;python解释器读到这句话是创建类(语法级别);类也是被创建出来的,通过type创建类,即类型的类型就是类型,默认为【class A(metaclass=type)】,可以说type就是这个类的元类

    class Student:
        pass
    s = Student()
    print(type(s))        #对象的类型是类
    print(type(Student))  #类的类型就是type
    输出:
    <class '__main__.Student'>
    <class 'type'>
    View Code

    #对于抽象类【class A(metaclass=ABCMeta)】可以说ABCMeta创建了这个A类,ABCMeta就是A的元类

    from abc import  ABCMeta
    class A(metaclass=ABCMeta):pass
    print(A)
    输出:
    <class '__main__.A'>
    View Code

    总结:type是所有类的元类,object是所有类的父类;元类是用来创建类的

      ⑤创建一个对象:类名()叫做实例化;__new__()创造了一个对象的空间;__init__进行一些简单的初始化工作

    1)类:class Leiming

    ①类是什么时候被记载的

      #类是从头到尾加载到内存中的(所以类的执行和有无实例化没有关系),如果类中有函数,只加载函数名,函数中的内容,只有在调用函数时才执行

    class A:
        name = "阿狸"
        print(name)
        def name(self):
            print("函数执行")
    输出:
    阿狸
    View Code

    ②类名是什么时候生效的

    #从头开始加载类,当把类中所有的变量加载完成后,才让类名去执行这个对象空间,所以在类中,使用类名去调用变量会报错

    class A:
        name = "阿狸"      
        print(A.name)   #语法报错,只有在类中所以内容加载完成,才让类名执行这个内存空间
    View Code

    注:静态属性/静态字段/静态变量、动态属性/动态方法是存在类空间中的,不在对象空间中存,在对象空间中只从这些变量和方法的一个地址,需要时去类空间查找(即对象空间存的不是func的内存地址,而是存着func内存地址的一个变量的地址)

    class A:
        name = "阿狸"
        def func(self):pass
    a = A()
    print(a.func)
    print(A.func)
    输出:(两个地址不一样)
    <bound method A.func of <__main__.A object at 0x000001824E7B5EB8>>
    <function A.func at 0x0000018257E2B048>

    2)对象

    ①可以通过指针找到类的空间中的内容

    ②对象本身内部也存储了一些只属于对象的属性(只存属性,不存方法)

    ③类创造对象的过程就是实例化的过程:构造为__new__;初始化为__init__

    3)组合:一个类的对象作为哦另一个类对象的属性(即什么有什么的关系,如人有武器)

    4)继承:主要为了节省代码;继承表示什么是什么的关系

    ①单继承和多继承

    #单继承:如果子类的对象调用调用某个方法,如果子类有,就调用子类的,如果子类没有,就调用父类的,如果还没有就报错;如果子类中有但是想要调用父类的,那么可以使用两种方法:

             super: 不用自己传self   格式:super(子类,self).方法名(除了self之外的参数)

             父类名:需要传self                格式:父类名.方法名(self,…)

    注:在任何类中调用的方法,都要仔细分辨一下这个self到底是谁的对象

    class Father:
        def __init__(self):       #self接受到的还是子类的对象,所以执行子类的func
            self.func()
        def func(self):
            print("In the Father...")
    class Son(Father):
        def func(self):
            print("In the son...")
    s = Son()
    输出:
    In the son...
    View Code

    #多继承

             新式类:默认继承object类,所以py3都是新式类;查找顺序遵循广度优先;可通过mro()方法查看继承顺序;在类的内部不用传子类名和self,如下super().func()

             经典类:没有继承object的类;查找顺序遵循深度优先;没有mro()方法;需要类名和self,如下super(子类名,self).func()

    ②抽象类和接口类的区别

             在python中接口类和接口类并没有特别大的区别,因为python是多继承的娥,而对于java而言,它是单继承的,只有抽象类中的方法,可以实现;但是接口中的所有方法只能写pass,接口支持多继承

    抽象类:抽象类中的方法是可以实现的,但是只能单继承

    接口类:可以单继承,但是这个类中的所有方法都不因该实现

             因为python是多继承的,所以怎么简单怎么写,没必要遵循上述要求

    5)封装

    ①广义的封装:把方法和属性都封装在一个类型中,定义一个规范来描述一类事物

    ②狭义的封装:私有化,只能在类的内部进行访问;__静态变量、私有方法、私有对象属性、私有的类方法、私有的静态方法;

    #在内存中通过【_类名__名字】形式存储;

    #在内的内部使用,就知道自己在哪个类中,所有在类的内部可以使用双下划线访问

    #在子类中不可以访问父类的私有变量;对于私有变量和方法,不能再类的外部使用也不能被继承

    4)多态

    ①鸭子类型:所以带同一方法的类都可以叫做鸭子类型;规范全凭自觉

    ②python中处处是多态

    #多态:一个类型的多种形态,如每个子类是父类的一种形态,一个父类被多个子类去继承,就是一种多态

    5)property

    ①property是一个装饰器函数,通过property可以将类中的方法伪装成属性/特性

    ②通过property将方法伪装成属性,可以使得程序逻辑性更加合理

    ③修改被property装饰的属性会调用这个装饰器的方法【@方法名.setter】,除了self外海有另一个参数:被修改的值

    ④删除被property装饰的属性会调用这个装饰器装饰的的方法【@方法名.deleter】,本质上属性是不能够被删除的,因为对象无法操作类中的静态属性(只是执行类装饰器装饰的方法)

    class A:
        @property
        def name(self):
            return "ali"
        @name.deleter
        def name(self):
            print("执行被@name.delerer装饰的方法")
    a = A()
    del a.name        #本质上对象不能删除静态属性,所以只是一个语法而已,没有实际意义
    print(a.name)
    输出:
    执行被@name.delerer装饰的方法
    ali
    View Code

    #可营造一个假象,高速用户把属性删除了(是个障眼法),这种方式比较偏其他语言,一般是将property和私有方法合用,这个时候更多的也会用到setter和deleter

    class A:
        def __init__(self,name):
            self.__name = name
    
        @property
        def name(self):
            return self.__name
        @name.deleter
        def name(self):
            del self.__name
    a = A("阿狸")
    print(a.name)       #阿狸
    del a.name        
    print(a.name)  #AttributeError: 'A' object has no attribute '_A__name'
    View Code

    #只用property:一个方法的计算结果本身就是一个属性,但是这个属性会随着这个类/对象的基础变量的变化而变化,此时可以将这个方法设置为属性

    class Circle:
        def __init__(self,r):
            self.r = r
        @property
        def area(self):
            return 3.14*self.r*self.r
    a = Circle(5)
    print(a.area)
    输出:
    78.5
    View Code

    #对于私有属性可以更改,但是可以对更改的值做约束,一是可以在类的内部再定义一个方法对私有属性进行更改,二是可以通过被装饰器【@方法名.setter】修饰的方法内部做约束

    class A:
        def __init__(self,age):
            self.__age = age
    
        @property
        def name(self):
            return self.__age
        @name.deleter
        def name(self):
            del self.__age
        @name.setter
        def name(self,new_age):
            if type(new_age) is int:
                self.__age = new_age
    a = A(18)
    print(a.name)
    a.name = 20    #调用方法给属性改值
    print(a.name)
    输出:
    18
    20
    View Code

    6)classmethod

    〇classmethod类方法的装饰器(内置函数)

    ①类方法,使用类名调用,默认传类名作为第一个参数

    ②不用对象命名空间中的内容,而用到了类命名空间中的变量(静态属性),或者类方法和静态方法,这种场景就用类方法

    #例:一个商场的购物程序(第一版,有问题)

    class Goods:
        __discount = 0.8       #活动促销前,打折率
        def __init__(self,price):
            self.__price = price
        @property
        def price(self):
            return self.__price*self.__discount
        def change_discount(self,num):     #此处相当于给对象空间添加了一个__discount新属性,而不是改变了类中的__discount私有静态属性
            self.__discount = num
    apple = Goods(10)
    banana = Goods(8)
    print(apple.price,banana.price)     #打折价格
    apple.change_discount(1)   #改变折扣,只改变的是apple的,而banana的没有变化
    print(apple.price,banana.price) #打折后的价格
    输出:
    8.0 6.4
    10 6.4
    View Code

    #若要以整个类的折扣为准,此时可以将对象调用私有静态属性改为以类名调用

    class Goods:
        __discount = 0.8       #活动促销前,打折率
        def __init__(self,price):
            self.__price = price
        @property
        def price(self):
            return self.__price*Goods.__discount
        def change_discount(self,num):     #此处相当于给对象空间添加了一个__discount新属性,而不是改变了类中的__discount私有静态属性
            Goods.__discount = num
    apple = Goods(10)
    banana = Goods(8)
    print(apple.price,banana.price)     #打折价格
    apple.change_discount(1)   #改变折扣,只改变的是apple的,而banana的没有变化
    print(apple.price,banana.price) #打折后的价格
    输出:
    8.0 6.4
    10 8
    View Code

    #对于以上程序,虽然实现了功能,但是恢复折扣时,是以apple对象调用的改变所有的商品的折扣,而这个对象也没有涉及到所有和apple对象操作相关的类,所以此时可以将change_discoun方法设置为静态方法,此时不需要任何实例化,就可以对商品中的所有商品进行折扣

    class Goods:
        __discount = 0.8       #活动促销前,打折率
        def __init__(self,price):
            self.__price = price
        @property
        def price(self):
            return self.__price*Goods.__discount
        @classmethod
        def change_discount(cls,num):     #此处相当于给对象空间添加了一个__discount新属性,而不是改变了类中的__discount私有静态属性
            cls.__discount = num
    apple = Goods(10)
    banana = Goods(8)
    print(apple.price,banana.price)     
    Goods.change_discount(1)   #静态方法,通过类名调用
    print(apple.price,banana.price) 
    输出:
    8.0 6.4
    10 8
    View Code

    #对于一个学生管理系统,假如有school 类、学生、老师、课程、班级四个类,对于登录这个方法来说,只有学生类、老师类(两个类基础school类)才会调用,此时可以将登录的方法写入schoole类,并设置为静态方法

    7)staticmethod

    ①如果一个类中的方法,既不需要用到self中的资源,也不用cls中的资源,相当于一个普通的函数,但是由于某种原因,还要把这个方法放到类中,这个时候,就可以将这个方法写成一个静态方法

    ②某种原因有:完全想用面向对象编程,所有的函数都必须写到类中;某个功能确实是这个类的方法,但是却没有用到和这个类有关系的任何资源

    8)反射

    ①从某个指定的命名空间中,用字符串数据类型的变量名来获取变量的值

    ②反射的分类

             类名反射:静态属性、类方法、静态方法

             对象反射:对象属性、方法

             模块:反射模块中的方法

             自己模块:反射自己模块

    ③方法:hasattr()、getattr()、setattr()、delattr()

    ④使用:使用反射时,变量名必须是拿到一个字符串的版本,可以从文件中、用户交互、网络传输中获取到字符串

    2.抽象类和接口类

    1)作用:规范子类当作必须实现某个方法

    2)抽象类和接口类不能被实例化

    3)python中有原生的实现抽象类的方法,但是没有原生实现接口类的方法

    4)抽象类和接口类的区别

             在python中接口类和接口类并没有特别大的区别,因为python是多继承的娥,而对于java而言,它是单继承的,只有抽象类中的方法,可以实现;但是接口中的所有方法只能写pass,接口支持多继承

    ①抽象类:抽象类中的方法是可以实现的,但是只能单继承

    ②接口类:可以单继承,但是这个类中的所有方法都不因该实现

             因为python是多继承的,所以怎么简单怎么写,没必要遵循上述要求

    3.内置方法(魔术方法/双下方法)

    1)格式:__名字__

    2)一般由内置函数、面向对象的特殊语法、python提高的语法糖调用

     #所有的简单符号实现(加减乘除等),都对应了左右两个数据类型对应的一个方法,加:__add__     减:__sub__  乘:__mul__ 除:__div__

    class Mytype:
        def __init__(self,s):
            self.s =s
    
        def __add__(self, other):
            return self.s.count('*') + other.s.count('*')
    obj1 = Mytype("ajdio****dnao")
    obj2 = Mytype("***sadi***")
    print(obj1.__add__(obj2))
    print(obj1+obj2)
    输出:
    10
    10
    View Code

    3)

    ①__str__   str(obj),要求obj必须实现__str__,要求这个方法的返回值必须是字符串

    ②__call__        对象()       要求使用装饰器

    ③__len__   len(obj),要求obj必须实现了__len__方法;要求这个方法的返回值必须是数字

    ④__new__   在实例化过程中,最先执行的方法,在执行init之前,用来创造一个对象

    ⑤__init__        在实例化的过程中,在执行new方法之后,自动触发的一个初始化方法

    4.__repr__方法

    1)

    引:对于str()方法,可以将数字转换为字符串,而对于repr()可以将字符串原形毕露

    n = 123
    n_new = str(n)
    s ="123"
    print(n_new)
    print(type(n_new))
    print(repr(s))
    输出:
    123
    <class 'str'>
    '123'
    View Code

    repr()方法的内部就是通过__repr__实现

    补充:格式化输入%s和%r的区别:%r是带引号输出

    print("----%s----" % ("abc"))
    print("----%r----" % ("abc"))
    输出:
    ----abc----
    ----'abc'----
    View Code

    2)__repr__对应得是repr(obj)这个方法和%r这个用法;__repr__是__str__的备胎,如果有__str__方法,那么print %s 、str()等操作都去执行__str__方法,并且使用__str__的返回值;如果没有__str__,那么print %s 、str()都会执行repr

    class A:
        def __init__(self,name):
            self.name =name
        def __str__(self):
            return "____%s____" %self.name
        def __repr__(self):
            return "____%r____" %self.name
    a = A("lol")
    print(str(a),repr(a))
    print("%s | %r" %(a,a))
    输出:
    ____lol____ ____'lol'____
    ____lol____ | ____'lol'____
    View Code

    注:如果__str__和__repr__两个方法只能使用一个,首先考虑__repr__

    3)在子类中使用__str__,先找子类的__str__,若子类没有就往上找(只要父类不是object,就执行父类的__str___),但是如果除了object之外的父类都没有__str__方法,就执行子类的__repr__方法,如果子类也没有repr方法,还要向上继续找父类的__repr-__方法,若一直找不到,再执行object类中的__str__方法

    class A:
        def __init__(self,name):
            self.name = name
        def __str__(self):
            return '**%s**'%self.name
        def __repr__(self):
            return self.name
    #
    class B(A):
        def __init__(self,name):
            self.name = name
        def __repr__(self):
            return '***'
    
    a = B('lol')
    print(a)
    print(str(a),repr(a))
    print('%s | %r'%(a,a))
    输出:
    **lol**
    **lol** ***
    **lol** | ***
    View Code
  • 相关阅读:
    分享一个Fluent风格的邮件发送封装类
    写一个ActionFilter检测WebApi接口请求和响应
    一道有趣的面试题,小鸟和火车的问题
    Centos7 查看Mysql配置文件
    Centos7 grep命令简介
    Centos7 网络配置
    django之python3.4及以上连接mysql的一些问题记录
    NetCore log4net 集成以及配置日志信息不重复显示或者记录
    ionic3中关于Ionic ui component使用的一些总结
    ionic2升级到ionic3并打包APK
  • 原文地址:https://www.cnblogs.com/piaolaipiaoqu/p/13904755.html
Copyright © 2020-2023  润新知