面向对象3-面向对象三大特征相关

1. 面向对象的三大特征

image

1.1 封装

封装主要体现在两个方面:

  • 将同一类方法封装到了一个类中,例如上述示例中:匪徒的相关方法都写在Terrorist类中;警察的相关方法都写在Police类中。
  • 将数据封装到了对象中,在实例化一个对象时,可以通过__init__初始化方法在对象中封装一些数据,便于以后使用。

2. 继承

  1. 什么是继承?
    继承就是新建类的一种方式
    被继承的类称为父类或基类,新建的类称为子类或派生类。
    子类可以使用父类的属性或者方法(继承)

  2. 为什么要用继承??
    类,解决了对象与对象之间的代码冗余的问题。
    继承,解决了类与类之间的代码冗余的问题。

  3. 如何使用继承???

    class Parent1():
        pass
    
    class Parent2():
        pass
    
    class Sub1(Parent1):
        pass
    
    class Sub2(Parent2):
        pass
    
    print(Sub1.__bases__) # 将所有继承的类打印出来
    print(Parents.__bases__)
    
    """
    新式类:继承了object类的子子孙孙类,都是新式类,
    py3默认继承object,都是新式类。
    经典类:没有继承object类的子子孙孙类都是经典类。
    """
    # py2中才区分新式类和经典类。
    
    

2. 继承的属性查找

2.1 单继承的属性查找

# 当前对象-->当前对象所在类-->当前对象所在类继承的类。

class Foo:
    def __f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.__f1()

class Bar:
    def __f1(self):
        print('Bar.f1')
		
		
obj = Bar()
obj.f2()  # Foo.f2---Foo.f1

2.2 多继承的属性查找

概述:
钻石顶端就是指菱形问题的顶端

  • 在python2.2之前,只支持经典类【从左到右,深度优先,大小钻石, 不留 顶端】

  • 后来,Python想让类默认继承object(其他语言的面向对象基本上也都是默认逗继承object),此时发现原来的经典类不能直接继承这个功能,有Bug。

  • 所以,Python决定不在原来的经典类上进行修改了,而是再创建一个新式类来支持这个功能。【从左到右,深度优先,大小钻石,留住顶端】。

    • 经典类,不继承object类型

      class Foo():
          pass
      
    • 新式类,之间或间接继承object

      class Base(object):
          pass
      
      class Foo(Base):
          pass
      
  • 这样,python2.2之后中就出现了经典类和新式类共存。(正式支持是2.3)

  • 最终,python3中丢弃经典类,只保留新式类。

详细文档:
	https://www.python.org/dev/peps/pep-0253/#mro-method-resolution-order-the-lookup-rule
	https://www.python.org/download/releases/2.3/mro/

In classic Python, the rule is given by the following recursive function, also known as the left-to-right depth-first rule.

def classic_lookup(cls, name):
    if cls.__dict__.has_key(name):
        return cls.__dict__[name]
    for base in cls.__bases__:
        try:
            return classic_lookup(base, name)
        except AttributeError:
            pass
    raise AttributeError, name
    
The problem with this becomes apparent when we consider a "diamond diagram":

      class A:
        ^ ^  def save(self): ...
       /   \
      /     \
     /       \
    /         \
class B     class C:
    ^         ^  def save(self): ...
     \       /
      \     /
       \   /
        \ /
      class D
      

Arrows point from a subtype to its base type(s). This particular diagram means B and C derive from A, and D derives from B and C (and hence also, indirectly, from A).

Assume that C overrides the method save(), which is defined in the base A. (C.save() probably calls A.save() and then saves some of its own state.) B and D don't override save(). When we invoke save() on a D instance, which method is called? According to the classic lookup rule, A.save() is called, ignoring C.save()!

This is not good. It probably breaks C (its state doesn't get saved), defeating the whole purpose of inheriting from C in the first place.

Why was this not a problem in classic Python? Diamond diagrams are rarely found in classic Python class hierarchies. Most class hierarchies use single inheritance, and multiple inheritance is usually confined to mix-in classes. In fact, the problem shown here is probably the reason why multiple inheritance is unpopular in classic Python.

Why will this be a problem in the new system? The 'object' type at the top of the type hierarchy defines a number of methods that can usefully be extended by subtypes, for example __getattr__().

(Aside: in classic Python, the __getattr__() method is not really the implementation for the get-attribute operation; it is a hook that only gets invoked when an attribute cannot be found by normal means. This has often been cited as a shortcoming -- some class designs have a legitimate need for a __getattr__() method that gets called for all attribute references. But then of course this method has to be able to invoke the default implementation directly. The most natural way is to make the default implementation available as object.__getattr__(self, name).)

Thus, a classic class hierarchy like this:

class B     class C:
    ^         ^  def __getattr__(self, name): ...
     \       /
      \     /
       \   /
        \ /
      class D
      

will change into a diamond diagram under the new system:

      object:
        ^ ^  __getattr__()
       /   \
      /     \
     /       \
    /         \
class B     class C:
    ^         ^  def __getattr__(self, name): ...
     \       /
      \     /
       \   /
        \ /
      class D


and while in the original diagram C.__getattr__() is invoked, under the new system with the classic lookup rule, object.__getattr__() would be invoked!

Fortunately, there's a lookup rule that's better. It's a bit difficult to explain, but it does the right thing in the diamond diagram, and it is the same as the classic lookup rule when there are no diamonds in the inheritance graph (when it is a tree).

总结:Python2和Python3在关于面向对象的区别。

  • Py2:

    • 经典类,未继承object类型。【从左到右,深度优先,大小钻石,不留顶端】

      class Foo:
          pass
      
    • 新式类,直接或间接继承object类型。【从左到右,深度优先,大小钻石,留住顶端--C3算法】

      class Foo(object):
          pass
      

      或者

      class Base(object):
          pass
      
      class Foo(Base):
          pass
      
  • Py3:

    • 新式类,丢弃了经典类只保留了新式类。【从左到右,深度优先,大小钻石,留住顶端--C3算法】

      class Foo:
          pass
      
      class Bar(object):
          pass
      

3. super()和 mro()列表

class People():
    school = 'SH'
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Student(People):
    
    def __init__(self, name, age, gender, course=None):
        if course is None:
            course = []
        # py2中的写法
        super(Student, self).__init__(name,age,gender)
        # super返回的是一个特殊的对象,只按照mro顺序向上查找
        # py3中的写法。
        super().__init__(name,age,gender)
        self.courses = course

    def choose_course(self, course):
        self.courses.append(course)
        print('{}选课成功{}'.format(self.name, self.courses))
	
	
stu = Student('ly', 22, 'female')
print(Student.__mro__)

正确理解按照mro顺序向上查找

4. 多态与多态性(了解)

多态即多种形态

多态带来的特性:
在不用考虑对象数据类型的情况下,直接调用对应的函数!!!

import abc  # abstract => 抽象的

"""
面试可能问的!!!
"""
# 抽象类:抽象类只能被继承,不能被实例化

class Animal(metaclass=abc.ABCMeta):
    
    @abc.abstractmethod  # 表示该方法已经是抽象方法了
    # 该方法限制继承Animal的子类里面必须有speak方法
    # (可以没有内容,但是必须定义有)py不推荐这样写
    def speak(self):pass
    # 抽象方法里面不要写函数体,直接pass

class People(Animal):
    def speak(self):
        print('嗷嗷叫')

    def Pig(Animal):
        print('哼哼')

    def Don(Animal):
        print('汪汪汪')


class People():
    def speak(self):
        print('嗷嗷叫')


class Pig():
    def speak(self):
        print('哼哼')
    
 
class Dog():
    def speak(self):
        print('汪汪')
   

class Txt():
    def speak(self):
        print('Txt')
        # 这就是动物

obj = People()
obj1 = Pig()
obj2 = Dog()
obj3 = Txt()

obj.speak()
obj1.speak()
obj2.speak()
obj3.speak()

# 此时可以
def animal(animal):
    return animal.speak()

animal(obj)
animal(obj1)
animal(obj2)
animal(obj3)


在不用考虑对象数据类型的情况下,直接调用对应的函数!!!!


len('abd')
len([1,2,334,4,4])

# 多态的举例说明
# 实际上
def len(itme):
    return item.__len__()

len('abj')

以后继承的父类中,如果像如下Animal中一样,说明要求继承的子类中一定要定义speak方法,这是不通过abc模块来实现的

# 父类限制子类的行为

class Animal(object):
    def speak(self):
        raise Exception('必须实现speak方法')

class People(Animal):
    pass

class Pig(Animal):
    def speak(self):
        print('哼哼')

class Dog(Animal):
    def speak(self):
        print('汪汪')

class Txt(Animal):
    def speak(self):
        print('Txt')

peo = People()
peo.speak()
# Exception: 必须实现speak方法

image

posted @ 2021-12-06 15:38  Joshua_jiaxue  阅读(62)  评论(0编辑  收藏  举报