Python面向对象三大特征(python系列20)

1.封装

        定义:

        数据角度:将基本数据类型复合成一个自定义类型

                作用:可读性更高,将数据与对数据的操作相关联。

        行为角度:对类外提供必要的功能,隐藏实现的细节

                作用:让调用者不必了解实现代码,也能调用我们写的功能

                让调用者操作变得简单

        私有化成员:

                定义:变量名双下划线开头

                如 self.__name = name

                本质:障眼法,可以通过对象._类名__成员名调用。

2.继承

        定义:重用现有类的功能,并在此功能上进行扩展。

        多个类型有相同的行为,且概念统一,这个时候我们就可以将多个类型统一的行为抽出来做一个类,而这个类就为多个类的父类,当创建子类时继承父类,就可以继承父类中已经有的行为。

通俗一些的讲,就是子类继承父类,就相当于子类复制粘贴了父类代码。

        继承语法:

class Person:
    pass
class Student(Person):
    Pass

# 代码1-1

       代码1-1中class Person其实就相当于,class Person(object),python代码中,创建一个类时,当不选择父类时,默认继承object,因此:任何类都直接或间接继承object类。

鸭子原则

其实:默认继承object体现了鸭子原则的精神,关心的是对象的行为,而不是对象的类型,只要对象的行为符合我们的期待,我们就可以将其视为所需要的类型。为什么代码1-1Person后面可以不写object来明确认父,这是一种思想,想告诉写python的程序员,我管你什么类型和继承关系,你给我实现功能就好了。

代码案例:

class Duck:  
    def quack(self):  
        print("Quack!")  
  
class Cat:  
    def meow(self):  
        print("Meow!")  
  
def sound(animal):  
    animal.quack()  # 调用Duck类中的quack方法  
  
duck = Duck()  
cat = Cat()  
  
sound(duck)  # 输出 "Quack!"  
sound(cat)   # 抛出 AttributeError,因为Cat类没有定义quack方法

   上述代码:

        因为Cat类没有定义quack方法,因此才抛出异常,若Cat类中有queck方法则正常输出,

        这里就能感受到,python只关心对象的行为,而不关心对象的类型,运行时才确定对象类型。而python中这种思想体现在动态类型语言检测和灵活的面向对象编程上。

总结:鸭子原则时一种思想,我们只关系对象的行为,而不关心对象的类型,而在python中这种思想体现在动态类型语言检测和灵活的面向对象编程上。在动态类型语言检测上,我们是在运行程序时才确定对象类型,因此当我们将一个对象当作参数传入一个函数中时,该对象可以时任何类型,只要能实现函数中的对象就不会抛出异常,反之不能满足函数中对象的行为则会抛出类型中没有此方法的异常。

在面向对象的继承上:

即使你不继承父类,但是你有和父类一样的行为,那么你就有了这个父类的特性,

从而可以在python中就可以达到你就是继承这个父类的效果。

类型判断:isinstance, type

class Animal:
    @staticmethod
    def eat():
        print("我吃吃吃")


class Dog(Animal):
    @staticmethod
    def run():
        print("我跑跑跑")


class Bird(Animal):
    @staticmethod
    def fly():
        print("我飞飞飞")


if __name__ == '__main__':
    bird = Bird()
    bird.fly()
    bird.eat()
    dog = Dog()
    dog.eat()
    dog.eat()
    animal = Animal()
    print(isinstance(dog, Dog))  # True
    print(isinstance(dog, Animal))  # True
    print(isinstance(animal, Dog))  # False
    print(type(dog) == Dog)  # True
    print(type(dog) == Animal)  # False
    print(type(animal) == Dog)  # False

# 代码1-2 

 通过上述代码:我们可以得出结论,type和isinstance的区别,用法上的不同,type是判断一个对象是不是一个类型,是就返回True,不是返回False。而isinstance是判断一个对象是不是属于一种类型,像代码1-2中,dog对象虽然是Dog类型,但是Dog继承了Animal类型,所以dog对象也属于动物类型,用isinstance判断返回的是True。反之,animal对象不属于Dog类型,怎么理解,只有说儿子属于爸爸,没有说爸爸属于儿子,animal对象是Animal类型是Dog类型的父类。

构造函数的继承

        若子类没有构造函数,将直接使用父类构造函数。

        若子类有构造函数,将覆盖父类的构造函数。

        若子类有构造函数,并且想使用父类的构造函数的成员,可以用super().__init__(参数,参数)

        python可以多继承,但是我们一般都使用单继承,多实现的思想来设计模型。

        继承父类变量的语法与代码

# 继承父类变量
class Car:
    def __init__(self, brand="", speed=""):
        self.brand = brand
        self.speed = speed


class Electric(Car):
    def __init__(self, brand="", speed="", capacity="", frequency=""):
        super().__init__(brand, speed)
        self.capacity = capacity
        self.frequency = frequency


electric = Electric("1", "1", "1", "1")
print(electric.capacity, electric.frequency, electric.brand, electric.speed)

# 代码1-3

        父类中是共性,子类中是个性。 

3.多态

        概念:对于父类的一个方法,在不同子类上有不同的体现。

        重写:目的是张显个性

        双下划线开头和双下划线结尾,这些是python的内置函数。

        重写内置函数:

        重写之前:

class Animal:
    def __init__(self, name="", color=""):
        self.name = name
        self.color = color


animal = Animal("dog", "white")
print(animal)  # <__main__.Animal object at 0x000001E829958948>

# 代码1-4

         重写之后:

class Animal:
    def __init__(self, name="", color=""):
        self.name = name
        self.color = color

    def __str__(self):
        return "name: %s, color: %s" % (self.name, self.color)


animal = Animal("dog", "white")
print(animal)  # name: dog, color: white

        面向接口编程:

        先确定用法,后决定做法,这是一种软件架构设计思想,

        在1991年python祖师爷就确定了print()的用法,就是打印一个对象__str__方法的返回值。

        这里的重写只是语法上,随便百度就能知道,并不值钱,而这里面牵涉到的软件架构思想才是重中之重,非常重要

        下列是经常会重写的内置函数:

        算数运算符:

        代码案例:

class Animal:
    def __init__(self, name="", age=0):
        self.name = name
        self.age = age

    def __str__(self):
        return "name: " + self.name + ", age: " + str(self.age)

    def __add__(self, other):
        if type(other) == int:
            return Animal(self.name, self.age + other)
        else:
            return Animal(self.name, self.age + other.age)


animal = Animal("dog", 2)
print(animal + 1)  # name: dog, age: 3
print(animal + animal)  # name: dog, age: 4

 在python中 animal + 1 相当于  animal.__add__(1)

我们再来看看重写__iadd__

class Animal:
    def __init__(self, name="", age=0):
        self.name = name
        self.age = age

    def __str__(self):
        return "name: " + self.name + ", age: " + str(self.age)

    def __iadd__(self, other):
        if type(other) == int:
            self.age += other
            return self
        else:
            self.age += other.age
            return self


animal = Animal("dog", 2)
print(id(animal))  # 140380752334848
animal += 1
print(id(animal))  # 140380752334848
print(animal)  # name: dog, age: 3

而__iadd__则是 +=, 同理加减乘除的累计运算符都是比算数运算符多个i

值得一提的是,当一个对象没有__iadd__时使用 += 对象会调用__add__,但是返回的也是一个新对象。而当一个对象没有__add__时,使用+对象时会抛出异常。

比较运算符对应的内置函数:

这里写两个比较常用的重写的代码案例:

"""
图书列表设计
类:BookModel
数据:书名 - name , 价格 - number
行为1,重写__eq__使其在列表容器中能够实现,remove, in , index等功能。
行为2, 重写__lt__ 或 __gt__ 使其在列表容器中能实现,sort 功能
"""


class BookModel:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        return self.name + " - " + str(self.price)

    def __eq__(self, other):
        return self.name == other.name

    def __lt__(self, other):
        return self.price < other.price


list_book = [
    BookModel("Java", 200),
    BookModel("Python", 100)
]

list_book.sort()
for item in list_book:  # Python - 100 Java - 200
    print(item)
    
list_book.remove(BookModel("Java", 200))
for item in list_book:  # Python - 100
    print(item)

__lt__:

当自定义类型重写了__lt__方法,自定义类型就可以比较大小,并且放入列表中还能使用列表的方法,如sort(),因为sort的实现代码是迭代列表,取列表中元素,让元素与元素之间比较,当列表中的对象类型没有重写__lt__方法时,会抛出异常。

__eq__: 

当自定义类型写__eq__方法,就可以根据自己的业务逻辑来判断自定义对象是否相等,值得一提的是object默认使用对象的内存进行比较,实现这个方法,如果将自定义对象存入列表,将可以使用列表的romove方法,index方法等。

  • 49
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值