zoukankan      html  css  js  c++  java
  • Python基础(2)面向对象

    面向对象 Object Oriented

    一、概述

      面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。面向对象编程(OOP)具体说是一种程序开发方法,一种编程思想、范式。你可以粗滤的理解为,项目经理干的活就是面向对象,负责分配任务,而coder干的活就是面向过程,负责具体任务的实施细节。

    1.面向过程:分析出解决问题的具体细节、步骤,然后逐步实现。

    1. 公式:程序 = 算法 + 数据结构
    2. 优点:所有环节、细节自己掌控。
    3. 缺点:考虑所有细节,工作量大。
    4. 找出解决问题的人,然后分配职责。

    2.面向对象:找出解决问题的人,然后分配职责,不关注实施细节。

    1.公式:程序 = 对象 + 交互

    2.优点

    (1) 思想层面:

    -- 可模拟现实情景,更接近于人类思维。

    -- 有利于梳理归纳、分析解决问题。

    (2) 技术层面:

    -- 高复用:对重复的代码进行封装,提高开发效率。

    -- 高扩展:增加新的功能,不修改以前的代码。

    -- 高维护:代码可读性好,逻辑清晰,结构规整。

    3.缺点:学习曲线陡峭。

    二、类和对象

    1.类(class):具有相同属性(实例变量)和行为(实例方法)的对象的抽象。实际上就是一种自定义的数据类型,类也有对应的类变量和类方法。

    2.对象(instance):类的具体实例,即归属于某个类别的”个体”。

    3.属性和行为:类与类方法不同,对象与对象属性值不同

    1.类和对象的创建

    (1)创建类

    1.格式

    class 类名(object):

    “””文档说明”””

    def _init_(self,参数列表):

    self.实例变量名= 参数(形参)

    def  实例方法名(self,参数列表):

    pass

    2.说明

    --   类名所有单词首字母大写,多个单词不用下划线隔开采用驼峰体。

    -- (object)是表示该类是从object类继承下来的,python3.x版本可省略

    -- __init__也叫构造函数,创建对象时被调用,也可以省略。

    -- self绑定的是被创建的对象的内存地址,名称可以随意。

    拓展:object类和type类的区别,即基类与元类的区别

    1.object类是继承层面的:其他的类或者对象都是通过继承的关系,直接或者间接的继承了object,翻阅所有对象的族谱,最后一定会发现它们的老祖宗就是object。

    1 >>>list.__base__    #Python为所有类都提供了一个__bases__属性,通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组
      (<class 'object'>,)
    2 >>>type.__bases__
      (<class 'object'>,)
    3 >>>object.__bases__   #object类是所有类的基类
      ()

    2.type类是类创建对象层面的:python中一切皆对象,数据、对象是由str、list等数据类型和自定义类创建的,而所有数据类型和自定义类都是由元类创建的

    1 >>> type(list)
      <class 'type'>
    2 >>> type(object)
      <class 'type'>
    3 >>> type(type)
      <class 'type'>    #type类是所有类的元类,但是其本身是由虚拟机创建的

    (2)创建对象(实例化)

    1.格式

    变量 = 类名 (实参数列表)

    2.说明

    --  python中无处不对象,对象是python中对数据的一种抽象的表示

     

    --  所有对象都有三种特性:id、类型、值

    id:对象的内存地址,由变量关联,查看通过:id(变量)

    类型:生成对象的模型,对应类名,查看通过:type(变量)

    值:对象中存放的数据,对应实参列表,查看通过:变量.__dict__

     

    --  每个对象都是由其对应的类创建出来的

    拓展:与对象的创建,调用,删除相关的魔法方法:__new__()方法,__init__()方法,__call__()方法,__del__()方法

    1.__new__()方法和__init__()方法:__new__()方法是一个类方法,在对象被创建的时候调用,该方法负责创建(实例化)一个对象。如果创建对象成功,该对象会自动的调用__init__()方法,对自身进行初始化,如果创建对象失败,没有对象了,自然也就没谁去调用__init__()方法了。

    2.__call__()方法:如果类中定义了该方法,可以使对象成为像函数和类一样的可调用对象。

     1 class Entity:
     2     '''
     3     调用实体来改变实体的位置。
     4     '''
     5 
     6     def __init__(self, size, x, y):
     7         self.x, self.y = x, y
     8         self.size = size
     9 
    10     def __call__(self, x, y):
    11         '''改变实体的位置'''
    12         self.x, self.y = x, y
    13 
    14 
    15 e = Entity(1, 2, 3)  # 创建实例
    16 print(e.__dict__)  # {'x': 2, 'y': 3, 'size': 1}
    17 e(4, 5)  # 实例可以象函数那样执行,并传入x y值,修改对象的x y
    18 print(e.__dict__)  # {'x': 4, 'y': 5, 'size': 1}

    3.__del__()方法:是python垃圾回收机制的实际应用,当类中定义了该方法时,创建的对象的引用计数为0时该方法被调用

    >>> class D(object):
        def __init__(self):
            print 'this is D.__init__()'
        def __del__(self):
            print 'this is D.__del__()'
    
    >>> d = D()
    this is D.__init__()
    >>> d2 = d
    >>> d3 = d
    >>> 
    >>> del d
    >>> del d2
    >>> del d3   #当对象的引用计数为0时自动触发__del__()方法
    this is D.__del__()

     

    2.实例成员

    (1)实例变量

    1.语法

    (1) 定义:对象地址.变量名=数据值

    (2) 调用:对象地址.变量名=数据值

    2.说明

    (1) 首次通过对象赋值为创建,再次赋值为修改。

    (2) 通常在__init__构造函数中(self.实例变量名=形参)创建,创建对象时赋值。也可以在

    (3) 每个对象存储一份,通过对象地址访问

     3.作用:描述对象自身的数据。

     4.__dict__对象的属性,用于存储自身实例变量(包括私有化变量)的字典。

    (2)实例方法

    1.语法

    (1) 定义: def  方法名称(self, 形参列表):

    方法体

    (2) 调用: 对象地址.实例方法名(实参列表)

    2.说明

    (1) 定义时至少有一个形参,一般命名为"self",用于绑定调用这个方法的对象地址,通过对象调用实例方法时self不用传参

    (2)每个对象共用一份,通过对象地址访问。不建议通过类名访问实例方法(对象与对象数据值不同,通过类名调用实例方法时,需要给self参数传参具体的对象)

    3.作用:表示对象行为。

     

     1 """
     2     实例成员
     3         记住一句话:实例成员,使用对象地址访问.
     4 """
     5 class Student:
     6     pass
     7 
     8 s01 = Student()
     9 # 定义实例变量: 对象.变量名 = 数据值
    10 s01.name = "lennie"
    11 print(s01.name)  #"lennie"
    12 s02 = Student()
    13 # print(s02.name)# AttributeError,因为s02指向的对象,没有创建过实例变量name
    14 # 通过__dict__获取当前对象的所有实例变量
    15 print(s01.__dict__)  #{'name': 'lennie'}
    16 print(s02.__dict__)  #{}
    17 
    18 
    19 class Student02:
    20     def __init__(self, name, height):
    21         self.name = name
    22         self.height = height
    23 
    24     # 实例方法
    25     def print_self(self):
    26         print(self.name,self.height)
    27 
    28 
    29 s01 = Student02("lennie", 170)
    30 s02 = Student02("ginger", 162)
    31 # 建议:实例方法,通过对象地址访问.
    32 s01.print_self()
    33 # 不建议:Student02.print_self(),若没有传递对象地址,实例方法不能正确访问对象数据.
    34 Student02.print_self(s02)

     

    3.类成员

    (1)类变量

    1.语法

    (1) 定义:在类中,方法外定义变量。

    class 类名:

       变量名 = 表达式

    (2) 调用:类名.变量名

          不建议通过对象访问类变量

    2.说明

    (1) 存储在类中。

    (2)所有对象共享一份,可通过类或者对象直接调用

    3.作用:描述所有对象的共有数据。

    (2)类方法

    1.语法

    (1) 定义:

        @classmethod

        def 方法名称(cls,形参列表):

             方法体

    (2) 调用:类名.方法名(实参列表)

          不建议通过对象访问类方法

    2.说明

    (1) 至少有一个形参,一般命名为'cls',用于绑定类的内存地址。

    (2) 使用@classmethod修饰的目的是调用类方法时可以隐式传递类,即通过类名调用类方法时cls不需要传参。

    (3) 类方法中不能访问实例成员,实例方法中可以访问类成员

    3.作用:操作类变量。

     

     1 """
     2     类成员,用类的地址去访问
     3 """
     4 class ICBC:
     5     # 类变量:总行的钱
     6     total_money = 1000000
     7     # 类方法
     8     @classmethod
     9     def print_total_money(cls):
    10         # print(id(cls), id(ICBC))
    11         # cls : 存储当前类的地址
    12         # print("当前总行金额:",ICBC.total_money)
    13         print("当前总行金额:", cls.total_money)
    14 
    15     def __init__(self, name, money):
    16         self.name = name
    17         self.money = money
    18         # 从总行扣除当前支行的钱
    19         ICBC.total_money -= money
    20 
    21 
    22 i01 = ICBC("天坛支行", 100000)
    23 i02 = ICBC("陶然亭支行", 100000)
    24 # 主流:通过类访问类成员
    25 ICBC.print_total_money()  #"当前总行金额: 800000"
    26 print(ICBC.total_money)   #800000
    27 # 非主流:通过对象访问类成员
    28 # print(i02.total_money)
    29 # i02.print_total_money()

     

    (3)静态方法

    1.语法

    (1) 定义:

        @staticmethod

        def 方法名称(形参列表):

                方法体

    (2) 调用:类名.方法名(实参列表)

          不建议通过对象访问静态方法

    2.说明

    (1) 使用@ staticmethod修饰的目的是该方法不需要隐式传参数,即参数列表不含selfcls参数,其实就是个函数

    (2) 静态方法不能访问实例成员和类成员

    3.作用:定义常用的工具函数

    三、三大特征

    1.封装

    (1)数据角度讲

    1.定义:将一些基本属性封装成共有属性,类的创建中的__init__初始化

    2.优势:将数据与对数据的操作相关联,代码可读性(相比容器)更高(类是对象的模板)。

    (2)行为角度讲

    1. 定义:类外提供必要的功能,隐藏实现的细节,私有化操作

    2. 优势:简化编程,使用者不必了解具体的实现细节,只需要调用对外提供的功能。

    3. 私有成员:

    (1) 作用:只有类内部可以直接访问,外部不能直接访问,可以对参数做检查,避免传入无效的参数

    (2) 做法:__成员名

    (3) 本质:障眼法,实际也可以访问。私有成员的名称被修改为:_类名__成员名,可以通过_dict_属性或dir函数查看。

     1 class Coder:
     2     """
     3     私有成员的读和写操作
     4     """
     5     def __init__(self, name, age):
     6         self.name = name
     7         self.__age = age  #私有化方式一:私有成员命名采用双下划线开头
     8         # self.set_age(age)  #私有化方式二
     9 
    10     # 使用两个公开的方法获取和修改私有属性
    11     def get_age(self):
    12         return self.__age  # 类内部访问无限制
    13 
    14     def set_age(self, value):
    15         if 0 <= value <= 100:  # 检验数据的有效性,限制随意更改属性值
    16             self.__age = value
    17         else:
    18             raise ValueError
    19 
    20     age=property(get_age,set_age)  #无读写限制
    21     # age=property(get_age,None)  #写限制
    22     # age=property(None,set_age)  #读限制
    23     # age=property(None,None)  #读写均限制
    24 
    25 c = Coder("lennie", 26)
    26 # 1.私有成员不能直接访问
    27 print(c.__age)  #AttributeError: 'Coder' object has no attribute '__age'
    28 print(c.__dict__)  # {'name': 'lennie', '_Coder__age': 26}
    29 # 2.可通过 对象._类名__成员名 访问(此种访问无限制)
    30 c._Coder__age = 27
    31 print(c._Coder__age)  # 27
    32 # 3.可通过两个私有方法实现私有成员的读写(此种方式可限制私有成员被随意改写)
    33 c.set_age(30)
    34 print(c.get_age())  # 30
    35 #4.可通过设置property(读方法名,写方法名),实现私有成员的读写
    36 c.age=32
    37 print(c.age)  #32

    4.属性@property

    公开的实例变量,缺少逻辑验证。私有的实例变量与两个公开的方法相结合,又使调用者的操作略显复杂。而属性可以将两个方法的使用方式像操作变量一样方便。

    (1) 定义:

    @property             #允许读,等价于定义类变量:属性名=property(读方法名,None)

     

    def 属性名(self):

    return self.__属性名

    @属性名.setter              #允许写,等价于定义类变量:属性名=property(读方法名,None)

    def 属性名(self, value):

    self.__属性名= value

    (2) 调用:

    对象.属性名 = 数据

    变量 = 对象.属性名

    (3) 说明:

    • 通常两个公开的属性,保护一个私有的变量。
    • @property 负责读取,@属性名.setter 负责写入
    •  只写:属性名= property(None, 写入方法名)
     1 class Coder:
     2     """
     3     @property和@私有成员名.setter
     4     """
     5     def __init__(self, name, age):
     6         self.name = name
     7         self.__age = age  #私有成员
     8 
     9     @property
    10     def age(self):
    11         return self.__age
    12 
    13     @age.setter
    14     def age(self, value):
    15         if 0 <= value <= 100:  # 检验数据的有效性,限制随意更改属性值
    16             self.__age = value
    17         else:
    18             raise ValueError
    19 
    20 c=Coder("lennie",26)
    21 # print(c.__age)  #依然不能直接调用,AttributeError
    22 #1.可直接由_类名__属性名调用
    23 c._Coder__age=27
    24 print(c._Coder__age)  #27
    25 #2.避免使用两个方法的繁琐,使私有成员如变量般调用
    26 c.age=30
    27 # c.age=120   #ValueError
    28 print(c.age)  #30

    (3)设计角度讲

    1. 定义:

    (1) 分而治之

    将一个大的需求分解为许多类,每个类处理一个独立的功能。 设计角度看类的创建应先考虑方法(行为,干什么),在考虑初始化(数据)

    (2) 变则疏之  #拆分的度

    变化的地方独立封装,避免影响其他类。

    (3) 高 内 聚

    类中各个方法都在完成一项任务(单一职责的类)

    (4) 低 耦 合    #低不是杜绝,是有互相调用才能完成

    类与类的关联性与依赖度要低(每个类独立),让一个类的改变,尽少影响其他类。

    2.优势:

    便于分工,便于复用,可扩展性强。

    2.继承

    (1)语法角度讲

    • 继承方法
    1. 语法:

    class 父类:

    def 父类方法(self):

        方法体

    class 子类(父类)

    def 子类方法(self):

    方法体

    儿子 = 子类()

    儿子.子类方法()

    儿子.父类方法()

    2.说明:子类直接拥有父类的方法.

    • 内置函数
    1. isinstance(对象, 类型返回bool值,判断对象是否是类的对象。
    2. issubclass(类型1,类型2)返回bool值,判断类型1是否是类型2子类。
     1 """
     2     继承 -- 方法
     3         编程:代码不用子类写,但是可以用.
     4 """
     5 class Person:
     6     def say(self):
     7         print("说话")
     8 
     9 class Student(Person):
    10     def study(self):
    11         print("学习")
    12 
    13 class Teacher(Person):
    14     def teach(self):
    15         print("讲课")
    16 
    17 # 创建父类型对象,只能访问父类型成员
    18 p01 = Person()
    19 p01.say()
    20 
    21 # 创建子类型对象,能访问父类型成员,还能访自身成员
    22 s01 = Student()
    23 s01.say()
    24 s01.study()
    25 
    26 # "是不是 实例"
    27 # s01的对象 是一种 Student类型
    28 print(isinstance(s01,Student))# True
    29 print(isinstance(s01,Person))# True    isinstance用于鉴定对象的直接模板以及其父模板
    30 print(isinstance(s01,Teacher))# False
    31 
    32 # s01的类型 等于 Student类型
    33 print(type(s01) == Student)# True
    34 print(type(s01) == Person)# False  type只能判定对象的直接模板
    35 
    36 # Student 类型 是一种 Person类型
    37 print(issubclass(Student,Person))# True
    38 print(issubclass(Student,Student))# True
    39 print(issubclass(Student,Teacher))# False   issubclass用于鉴定两个类是否有亲属关系
    • 继承数据
    1. 语法

    class 子类(父类):

    def __init__(self,参数列表):

    super().__init__(参数列表)

    self.自身实例变量 = 参数

    2.说明

    子类如果没有构造函数,将自动执行父类的,但如果有构造函数将覆盖父类的,此时必须通过super()函数调用父类的构造函数,以确保父类实例变量被正常创建。

    • 定义

    重用现有类的功能,并在此基础上进行扩展。子类直接具有父类的成员(共性),还可以扩展新功能

     1 """
     2     继承 -- 数据
     3 """
     4 class Person:
     5     def __init__(self, name=""):
     6         self.name = name
     7 
     8 class Student(Person):
     9     def __init__(self, name="", score=0):
    10         # self.name = name
    11         # 如果子类没有构造函数,使用父类构造函数
    12         # 如果子类有构造函数,必须铜通过super()调用父类构造函数,
    13         # 否则会覆盖父类(不执行)的.
    14         super().__init__(name)
    15         self.score = score
    16 
    17 
    18 # 创建实例变量
    19 s01 = Student("lennie", 100)
    20 print(s01.name)  #"lennie"
    21 print(s01.score)  #100
    22 p01 = Person("ginger")
    23 print(p01.name)  #"ginger"
    24 print(p01.score) #报错AttributeError,子直接继承父,但父不能调用子类成员
    • 优点

    一种代码复用的方式。

    • 缺点

    耦合度高:父类的变化,直接影响子类

    (2)设计角度讲

    • 定义

    将相关类的共性进行抽象,统一概念,隔离变化。类是对象的抽象,同理父类是子类的抽象。

    • 适用性

    多个类在概念上是一致的,且需要进行统一的处理。

     

     1 """
     2     继承 -- 设计角度。隔离变化
     3         老张开车去东北.
     4         需求变化:还可能坐飞机,坐火车......
     5 """
     6 
     7 
     8 class Person:
     9     def __init__(self, name=""):
    10         self.name = name
    11 
    12     def go_to(self,str_position,vehicle):
    13         #通过一个参数vehicle将对象传递进来,从而调用方法transport(),
            鸭子类型的概念(此处传入的对象只要有transport()方法,就视为也是Vehicle类型,并非一定是其子类)
    14 print(self.name,vehicle.transport(),"",str_position,sep="") 15 16 17 class Vehicle: 18 """ 19 交通工具类,抽象的 20 将共有的功能(运输)进行提取,供其子类对象继承复用 21 """ 22 def transport(self): 23 pass 24 25 # -------------------------------------隔离变化 26 class Car(Vehicle): 27 28 def transport(self): 29 return "坐车" 30 31 class Airplane(Vehicle): 32 33 def transport(self): 34 return "坐飞机" 35 36 37 c01 = Car() 38 a01 = Airplane() 39 lz = Person("老张") 40 lz.go_to("东北",c01) #老张坐车去东北 41 lz.go_to("东北",a01) #老张坐飞机去东北

     

     

    • 相关概念
    1. 父类(基类、超类)、子类(派生类)。
    2. 父类相对于子类更抽象,范围更宽泛;子类相对于父类更具体(功能更强大),范围更狭小。
    3. 单继承:父类只有一个(例如 JavaC#)。
    4. 多继承:父类有多个(例如C++Python)。
    5. Object类:任何类都直接或间接继承自 object 类。
    • 多继承

    一个子类继承两个或两个以上的基类,父类中的属性和方法同时被子类继承下来。

    MRO(Method Resolution Order)查看同名方法的解析顺序:print(D.mro)类自身 --> 父类继承列表(由左至右)--> 再上层父类

          A

         /   

        B     C

            /

          D

     1 class A:
     2     pass
     3 class B(A):
     4     pass
     5 class C(A):
     6     pass
     7 class D(B,C):
     8     pass
     9 
    10 print(D.mro())#[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    11 print(C.mro())#[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    12 print(B.mro())#[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    13 print(A.mro())#[<class '__main__.A'>, <class 'object'>]

    3.多态

    (1)设计角度讲

    • 定义

    父类的同一种动作或者行为,在不同的子类上有不同的实现

    • 作用
    1. 在继承的基础上,体现类型的个性化(一个行为有不同的实现)。
    2. 增强程序扩展性,体现开闭原则。

    (2)语法角度讲

    • 重写

    子类中定义(def)了父类中同名的方法(方法名、参数)。虽然该同名方法在子类中定义了,依然认为是父类的方法。

    在调用该方法时,实际执行的是子类的方法。

    • 快捷键

    Ctrl + O

    • 内置可重写函数

    Python中,以双下划线开头、双下划线结尾的是系统定义的成员。我们可以在自定义类中进行重写,从而改变其行为。

    • 转换字符串

    __str__函数:将对象(打印时显示内存地址)转换为字符串(打印对象时显示自定义字符串)(对人友好的)

    __repr__函数:将对象转换为字符串(解释器可识别的)

     

     1 """
     2     内置可重写函数
     3         自定义对象 -->  str
     4 """
     5 class Car:
     6     def __init__(self, brand="", price=0,max_speed = 0):
     7         self.brand = brand
     8         self.price = price
     9         self.max_speed = max_speed
    10 
    11     # # 对人友好的,随心所欲的规定字符串内容-->将<__main__.Car object at 0x...>转换为自定义的字符串
    12     # def __str__(self):
    13     #     return "品牌是%s,单价是%d"%(self.brand,self.price)
    14 
    15     # 对解释器友好,根据python语法规定字符串内容-->功能同__str__(),但是更强大,通常与eval("")配合使用
    16     def __repr__(self):
    17         return "Car('%s',%d,%d)"%(self.brand,self.price,self.max_speed)
    18 
    19 # 应用:将对象换成自定义的字符串显示出来
    20 c01 = Car("宝马",1000000,260)
    21 print(c01)  #"品牌是宝马,单价是1000000",会自动调用c01.__str__()方法
    22 #当已经定义了__str__()时必须显式调用c01.__repr__()或者repr(c01)才会执行
    23 print(c01.__repr__())  #"Car('宝马',1000000,260)"

     

    拓展:eval("代码")函数

    1. 功能:将字符串中的内容(去掉引号)直接作为代码运行
    2. 参数:python代码格式的字符串,否则报错
    3. 返回值:字符串内容代码的执行结果
    >>> eval("1+2")
    3
    >>> eval("lennie")          #等价于在命令行直接输入lennie
    NameError: name 'lennie' is not defined
    >>> lennie
    NameError: name 'lennie' is not defined
    >>>

    4.应用:eval("")函数与__repr__()方法配合使用,实现深拷贝对象

     

     1 # 定义技能类(技能名称,攻击比例,持续时间)
     2 # 创建技能对象,直接print对象.
     3 # 克隆(深拷贝)技能对象,体会改变其中一个,不影响另外一个.
     4 
     5 class Skill:
     6     def __init__(self, name="", atk_ratio=0.1, duration=0.1):
     7         self.name = name
     8         self.atk_ratio = atk_ratio
     9         self.duration = duration
    10 
    11     def __str__(self):
    12         return "%s---%d---%d" % (self.name, self.atk_ratio, self.duration)
    13  
    14     def __repr__(self):
    15         return "Skill('%s',%d,%d)" % (self.name, self.atk_ratio, self.duration)
    16 
    17 
    18 s01 = Skill("佛怒火莲", 3, 5)
    19 print(s01)     #佛怒火莲---3---5
    20 
    21 # s02 = eval("Skill('佛怒火莲',3,5)")
    22 s02 = eval(s01.__repr__())
    23 s01.name = "焰分噬浪尺"
    24 print(s02)     #佛怒火莲---3---5

     

    • 运算符重载

    定义:让自定义的类生成的对象(实例),能够像str、int、list类型数据对象一样,使用运算符进行操作。

      • 算数运算符

      • 反向算数运算符重载

      • 复合运算符重载(不产生新对象)

      • 比较运算重载

     1 """
     2     运算符重载(重写)
     3 """
     4 
     5 # 多态:调用父,执行子.
     6 class Vector:
     7     """
     8         向量
     9     """
    10 
    11     def __init__(self, x=0):
    12         self.x = x
    13 
    14     def __str__(self):
    15         return "分量是:" + str(self.x)
    16 
    17     # 算数运算符 --> 返回新结果
    18     def __add__(self, other):
    19         """
    20              当前对象与其他数据相加时被自动调用
    21         :param other: 其他数据
    22         :return: 向量
    23         """
    24         # 如果other 是 向量
    25         if type(other) == Vector:
    26             return Vector(self.x + other.x)
    27         # 否则
    28         else:
    29             return Vector(self.x + other)
    30 
    31     def __rsub__(self, other):
    32         return Vector(other - self.x)
    33 
    34     # 复合运算符 --> 在原有对象基础上修改
    35     def __iadd__(self, other):
    36         self.x += other
    37         return self
    38 
    39     def __gt__(self, other):
    40         return self.x  > other.x
    41 
    42 # 1. 算数运算符
    43 v01 = Vector(10)
    44 # 自定义向量 + 整数  -->  向量
    45 print(v01 + 5)  # v01.__add__(5)
    46 # 自定义向量 + 自定义向量  -->  向量
    47 v02 = Vector(2)
    48 print(v01 + v02)
    49 
    50 # 2. 反向算数运算符
    51 # 整数 - 自定义向量 --> 向量
    52 print(5 - v01)  # v01.__rsub__(5)
    53 
    54 """
    55 list01 = [1]
    56 
    57 # print(id(list01))
    58 list01 += [2]# 在原有基础上增加新元素
    59 # print(id(list01))
    60 
    61 # print(id(list01))
    62 list01 = list01 + [3]# 产生新的对象
    63 # print(id(list01))
    64 
    65 print(list01)
    66 """
    67 
    68 # 3. 复合运算符
    69 #       默认使用算数运算符
    70 print(id(v02))
    71 v02 += 3  # v02 = v02 + 3
    72 print(v02)
    73 print(id(v02))
    74 
    75 # 4. 比较运算符
    76 # 自定义类 > 自定义类
    77 print(v01 > v02)# v01.__gt__(v02)

    四、六大设计原则

    1.开-闭原则(目标、总的指导思想)

    Open Closed Principle

    对扩展开放,对修改关闭。

    增加新功能,不改变原有代码。

    2.类的单一职责(一个类的定义)

    Single Responsibility Principle   

    一个类有且只有一个改变它的原因。

    3.依赖倒置(依赖抽象)

    Dependency Inversion Principle

    客户端代码(调用的类)尽量依赖(使用)抽象。

    抽象不应该依赖细节,细节应该依赖抽象。

    4.组合复用原则(复用的最佳实践)

    Composite Reuse Principle

    如果仅仅为了代码复用优先选择组合复用,而非继承复用。

    组合的耦合性相对继承低。

    5.里氏替换(继承后的重写,指导继承的设计)

    Liskov Substitution Principle

    父类出现的地方可以被子类替换,在替换后依然保持原功能。

    子类要拥有父类的所有功能。

    子类在重写父类方法时,尽量选择扩展重写,防止改变了功能。在Ctrl+o时,别删东西,重写后扩展重写

    6.迪米特法则(类与类交互的原则)

    Law of Demeter

    不要和陌生人说话。

    类与类交互时,在满足功能要求的基础上,传递的数据量越少越好。因为这样可能降低耦合度(利用抽象去调用子类,隔离变化)。

     1 """
     2     面向对象的应用:
     3         定义员工管理器,记录所有员工,计算总工资.
     4         程序员:底薪 + 项目分红
     5         测试员:底薪 + bug * 5
     6         要求:增加新的岗位,不影响员工管理器.
     7         指出:三大特征,六个原则.
     8 
     9         封装:员工管理器,程序员,测试员
    10         继承:员工隔离员工管理器与具体员工的变化
    11         多态:员工管理器调用员工,执行程序员,测试员.
    12         开闭:增加新的岗位,不影响员工管理器.
    13         单一:员工管理器(管理员工),员工(隔离变化),程序员(计算薪资),测试员(计算薪资)
    14         依赖倒置:员工管理器调用员工,而不调用程序员/测试员
    15         组合复用:员工管理器存储具体员工的变量
           里氏替换:程序员(继承员工底薪后添加奖金),测试员(继承员工底薪后添加bug提成)
           迪米特法则:员工管理器与程序员,测试员的交互,改由与员工交互,减少了数据的传递
    16 """ 17 18 19 class EmployeeManager: 20 """ 21 员工管理器 22 """ 23 24 def __init__(self): 25 self.__all_employee = [] 26 27 def add_employee(self, emp): 28 # 判断 emp 是员工 则 添加....? 29 # if type(emp) == Employee: 30 # emp 属于 员工 31 if isinstance(emp, Employee): 32 self.__all_employee.append(emp) 33 34 def get_total_salary(self): 35 total_sarlay = 0 36 for itme in self.__all_employee: 37 # 调用的是员工 38 # 执行的是程序员/测试员 39 total_sarlay += itme.calculate_salary() 40 return total_sarlay 41 42 43 class Employee: 44 """ 45 员工 46 抽象的 --> 统一概念 --> 隔离变化 47 48 """ 49 50 def __init__(self, base_salary): 51 self.base_salary = base_salary 52 53 def calculate_salary(self): 54 """ 55 计算薪资 56 :return: 小数类型的薪资 57 """ 58 return self.base_salary 59 60 61 class Programmer(Employee): 62 """ 63 程序员 64 """ 65 66 def __init__(self, base_salary=0, bonus=0): 67 super().__init__(base_salary) 68 self.bonus = bonus 69 70 def calculate_salary(self): 71 # return self.base_salary + self.bonus 72 return super().calculate_salary() + self.bonus 73 74 75 class Tester(Employee): 76 def __init__(self, base_salary=0, bug_count=0): 77 super().__init__(base_salary) 78 self.bug_count = bug_count 79 80 def calculate_salary(self): 81 return super().calculate_salary() * self.bug_count * 5 82 83 # def calculate_salary(self): 84 # return self.base_salary * self.bug_count * 5 85 86 manager = EmployeeManager() 87 p01 = Programmer(32000, 50000) 88 manager.add_employee(p01) 89 manager.add_employee(Tester(8000, 2)) 90 print(manager.get_total_salary())
  • 相关阅读:
    Element+Vue.js 选择器常用属性
    常用xml头文件
    【链接】调查显示:超20%美国大学生曾花学生贷款投
    Quartz遇到的问题
    List去重
    SpringDataJPA
    IDEA:Error during artifact deployment. See server log for details.详解
    Quartz定时任务
    多线程条件通行工具——CountDownLatch
    多线程同步工具——Lock
  • 原文地址:https://www.cnblogs.com/lennie-luo/p/11629301.html
Copyright © 2011-2022 走看看