07-Python之面向对象编程(三大特性)

# 面向对象的三大特性

- 封装
- 继承
- 多态

## 1 封装

- 封装就是对对象的成员进行访问限制。
- 封装的三个级别:
  - 公开的:public
  - 受保护的: protected
  - 私有的:private
  - 注意:public,protected,private不是关键字
- 判断对象的位置:对象内部、对象外部、子类中和跨模块访问

- 公开的:

  - 公共的封装实际对成员没有任何操作,没有限制,任何地方都可以使用

- 受保护的封装:

  - 受保护的封装是将对象成员进行一定级别的封装,
  - 在类中或者子类中都可以访问,
  - 在外部不可以访问:此变量不能通过from XXX import xxx 导入
  - 封装方法:在成员名称前添加一个下划线就可以

- 私有的:

  - 私有成员时最高级别的封装,只能在当前类或对象中访问
  - 在成员面前添加两个下划线__.
  - 私有化方法(私有化函数)和私有化成员一样
  - 私有成员只是一种改名策略,可以使用.__dict__查看改名后的内容

  * 案例1.1

# 封装到对象中的属性是一种封装。 类本身就算是一种封装
class Person:
    
    country = 'China'    # 公开的 
    _addr = 555          # 被保护的
    __sch = "zhkai"      # 私有的
    
    def __init__(self, sex, name, age):
        self.name = name   # 公开的
        self._sex = sex    # 被保护的
        self.__age = age   # 私有的
        
    def func(self):       # 公开的
        print(self.name)
        
    def _func1(self):     # 被保护的
        print(self._sex)
        
    def __func2(self):    # 私有的
        print(self.name)
        
    def func3(self):      # 私有的可以在类内部进行访问
        print("__sch:", self.__sch, "  ","__age:", self.__age)
        
# 实例化对象    
p1 = Person("man", 'xiaming', 39)
p2 = Person("girl",'lisi', 18)

"""-------------------------------------------------------"""
# 1.公开的没有限制
p1.func()    # xiaming
print(p1.country, p2.country, Person.country)   # China China China
"""-------------------------------------------------------"""

# 2.被保护的能在类中调用
    # 2.1在类中或者子类中都可以访问
    # 2.2在外部不可以访问:此变量不能通过from XXX import xxx 导入   
    
p2._func1()   # girl
print(p1._addr, p2._addr, Person._addr)  # 555 555 555
"""-------------------------------------------------------"""

# 3.私有的

    # 3.1父类以及类的外部不能访问
# print(Person.__name)  
# p1 = Person()
# print(p1.__name)

    # 3.2 在类的内部可以调用
p1.func3()  # __sch: zhkai    __age: 39
p2.func3()  # __sch: zhkai    __age: 18

    # 3.3通过改名访问
        # 执行类的内容时,发现”__“的变量名时,系统会自动将其改名
        # 可以使用.__dict__查看,发现改名为 classname._classname__attributename 
        # 例如:__age 改为 _Person__age
p2._Person__func2()  # lisi
print(p2._Person__age, p2.__dict__['_Person__age'], Person.__dict__['_Person__sch']) 
       # 18 18 zhkai

- property()修饰器函数:

  - property(fget=None, fset=None, fdel=None, doc=None)
  - 第一个参数时获得属性的方法名。
  - 第二个参数是设计属性的方法名。
  - 第三个参数是删除属性的方法名。
  - 返回一个设置属性的属性。

  * 案例1.2

class Person:
    def __init__(self,name):
        self.name = name
    def getName(self):
        return self.name
    def setName(self, value):
        self.name = value
    def delName(self):
        del self.name
    x = property(getName,setName,delName)

p = Person("dd")
print(p.x) # dd
# 如果p 是 Person的实例化, p.x 将触发 getter,p.x = value 将触发 setter , del p.x 触发 deleter。
p.name = 55
print(p.x) # 55

## 2 继承性

- 1.概念:

  - 在继承关系中,原来设计好的称作父类,新设计的类称作子类。
  - 继承是为了代码复用和设计复用而设计的
  - 继承就是一个类可以获得另外一个类中的成员属性和成员方法。
  - 继承与被继承:
    - 被继承的类叫父类,也叫基类,也叫超类
    - 用于继承的类,叫做子类,也叫派生类
    - 继承与被继承一定存在一个is-a的关

- 2.继承的语法:

  class 类名(被继承的类):

- 3.单继承和多继承:

  - 概念:
    - 单继承:每个类只能继承一个类,仅仅继承一个父类
    - 多继承:每个类允许继承多个类,继承了多个父类
      - 无重叠的多继承
      - 有重叠的多继承
  - 优缺点
    - 单继承:
      - 传承有序逻辑清晰语法简单隐患少
      - 功能不能无限扩展,只能在当前唯一的继承链中扩展
    - 多继承:
      - 优点:类的扩展功能方便
      - 缺点:继承关系混乱

  * 案例2.1简单的继承

# 1.简单的继承
class A():  
    pass
class C():
    pass

# 1.1单继承
class B(A):   
    pass

# 1.2多继承
class Person(A, C):
    pass

# 注意:创建了类就会继承object
# 1.3查看对象
print(B.__class__)
print(type.__class__)

# 1.4查看父类:使用__bases__
print(B.__bases__)      # 单继承,只有一个父类
print(Person.__bases__)   # 多继承,包含了两个父类
print(int.__bases__, bool.__bases__)
"""
# 结果
<class 'type'>
<class 'type'>
(<class '__main__.A'>,)
(<class '__main__.A'>, <class '__main__.C'>)
(<class 'object'>,) (<class 'int'>,)
"""

- 4.资源的继承:

- 资源的使用:

  - 所有的类都继承object类,即所有的类都是object类的子类。
  - 子类可以继承它的父类的任何属性和方法,可以使用父类里除了私有成员外所有内容。
  - 子类只是通过引用关系访问父类的资源,只能读取,而不是修改。

* 案例2.2

# 2.1资源的使用
# 定义父类(超类)
class Person():
    name = "China"         # 公有的
    __age = 18             # 私有的
    _addr = 56       # 被保护的
    def sleep(self):
        print("Sleeping....")
        
# 定义子类
class Teacher(Person):
    teacher_id = "9527"
    def make_test(self):
        print("attention")

t = Teacher() 

# 1.子类可以继承它的父类的任何属性和方法,可以使用父类里除了私有成员外所有内容
    # 1.1访问公有的、被保护的属性和方法
print(t.name, t._addr)  # China 56
t.sleep()  # Sleeping....

    # 1.2无法访问私有属性和方法
# print(t.__age)   # 会报错  但是还是可以通过改名访问print(t._Person__age)

    # 1.3 子类实例化对象访问自己的属性和方法
print(t.teacher_id)  #  9527
t.make_test()   # attention

    # 1.4用父类的资源,只能读取,而不是修改
         # Teacher中没有name的属性,利用Teacher.__dict__查看
         # 给Teacher增加了name属性,没有修改Person的name
Teacher.name = "201888" 
         # 修改后
print(Teacher.__dict__) # 'name': '201888'
print(Person.__dict__)  # 'name': 'China',

- 资源的覆盖:

  - 子类中定义与父类同名的方法或属性
  - 则优先使用子类,会自动覆盖父类对应的方法或属性。

  * 案例2.3

class B():
    age = 18
    
class A(B):
    age = 28   

t = A()
print(A.__dict__)
print(A.__dict__)
# 子类和父类定义同一个名称变量,则优先使用子类
print(t.age)  
"""
# 结果
{'__module__': '__main__', 'age': 28, '__doc__': None}
{'__module__': '__main__', 'age': 28, '__doc__': None}
28
"""

- 资源的累加:

  - 在一个类的基础上增加一些额外的资源。
  - 子类相比父类多一些自特有的资源,子类中可以定义独有的成员属性和方法
  - 被覆盖的额基础上,新增内容
  - 子类如果扩充父类方法
    - 可以在定义新方法的同时访问父类成员来进行代码重用,
    - 可以使用 [父类名.父类成员]的格式来调用父类成员,
    - 也可以使用super(),父类成员的格式调用

  * 案例2.4

# 1.通过实例化对象增加
class Hum():
    a = 1
    def __init__(self):
        self.b = 2    
        
class Person(Hum):
    c= 3  
p = Person()
p.a = 77   
print(p.__dict__)
"""--------------------------------------------------------------------------"""

# 2.通过父类,在父类内增加
    # 注意;这种情况是子类没有构造函数,不能覆盖父类的构造函数
class Hum():
    a = 1
    def __init__(self):
        self.b = 2
        self.a = 77  # 增加的属性    
        
class Person(Hum):
    c= 3 
    
p = Person()
print(p.__dict__)

"""--------------------------------------------------------------------------"""
# 3.通过子类,在子类内增加
    # 注意:如果子类有构造函数,会覆盖父类的构造函数的。
class Hum():
    a = 1
    def __init__(self):
        self.b = 2
        
class Person(Hum):
    c= 3 
    def __init__(self):    
        Hum.__init__(self)   
            # 引用B中的实例化属性,解决被覆盖的问题 
        self.a = 77  # 增加的属性
    
p = Person() 
print(p.__dict__)

"""--------------------------------------------------------------------------"""

# 4.通过super
class Hum():
    a = 1
    def __init__(self):
        self.b = 2
        
class Person(Hum):
    c= 3 
    def __init__(self):    
        super(Person, self).__init__()  # 实现父类功能   
        # super().__init__() # 这种写法也可以
        self.a = 77
p = Person() 
print(p.__dict__)

"""--------------------------------------------------------------------------"""
# 5子类扩充夫类功能
    # 直接调用父类相应的功能,或者通过super
class Person():
    def sleep(self):
        print("Sleeping....")
    def work(self):
        print("make some money")
        
class Teacher(Person):
    def make_test(self):
        print("attention")
        
    def work(self):
        # 扩充父类功能只需调用父类相应的函数
        # Person.work(self)
        super().work()
        self.make_test()# 此处是扩充
        # 扩充父类的另一种方法,super 代表得到父类
                        
t = Teacher() # make some money
t.work()  # attention

- 5.构造函数

- 是一类特殊的函数,在进行实例化之前进行调用
- 如果定义了构造函数,则实例化时使用构造函数,不查找父类构造函数
- 如果没有定义,则自动查找父类构造函数
- 如果子类没定义,父类构造函数带参数,则构造对象时的参数应该按父类参数构造

- 6.继承变量函数的查找顺序问题:

- 任何情况优先查找自己的变量
- 自己没有就查找父类
- 构造函数如果本类中没有定义,则自动查找调用父类构造函数
- 如果本类有定义,则不继续往上查找

* 案例2.5

# 继承里面的构造函数
class A():
    def __init__(self):
        print("A__init__")
class B():
    def __init__(self):
        print("B__init__")
class C(B):
    def __init__(self):
        print("C__init__")
class D(A):
    pass
class E():
    pass
 
p1 = A() # 自动调用A的构造函数
p2 = D() # 自动调用A的构造函数,
         # 因为D中没有构造函数,只能调用父类的构造函数
p3 = C() # 自动调用C的构造函数
         # 父类B的构造函数被覆盖了,C中已经存在各种函数
# p3 = E() # 会报错,E()没构造函数

- 7.super

- super()的作用是:
  - 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,
  - 但有时,我们希望能同时实现父类的功能,
  - 这时,我们就需要调用父类的方法了,可通过使用 super 来实现。
- super(参数1,[参数2])
  - 沿着参数2的MRO链条,找参数1 的节点
  - 切记不要使用动态参数,self.__class__,容易出错
- super不是关键字,而是一个类
- super的作用时获取MRO(MethodResolustionOrder)列表中的第一个类
- 与父类没有实质关系,可以通过super 调用父类
- super 的两个使用方法:参见构造函数中调用父类的构造函数
- 沿着MRO链条,找到下一级节点,去调用对应的方法

## 3多态

- 多态就是同一个对象在不同情况下由不同的状态出现
- 一个类所延伸的多种形态
- 调用时的多种形态
- 多态不是语法,是一种设计思想
- 多态性:一种调用方式,不同执行效果
- 多态:同一种事物的多种形态,
- [多态和多态性](http://www.cnblogs.com/luchuangao/p/6739557.html)

- Mixin设计模式
  - 主要采用多继承方式对类的功能进行扩展
  - 网上阅览资料
- 我们使用多继承语法来实现Mixin
- 使用时
  - 首先必须表示某一单一功能
  - 职能必须单一,如果有多个,则写多个Mixin
  - Mixin不能依赖子类的实现
  - 子类即使没有继承这个Mixin类,照样能工作,只是缺少了某个功能
- 优点
  - 可以在不对类进行修改的情况下,扩充功能
  - 可以方便的组织和维护不同功能组建的划分
  - 可以根据需要任意调整功能类的组合
  - 可以避免创建很多新的类,导致类的继承混乱

posted @ 2019-07-25 23:11  xiaoou7  阅读(224)  评论(0编辑  收藏  举报