Python 多态



Python 多态

搞懂Python类的多态性

Python中多态是指对象有多种形态。比如动物有多种形态,狗,猫,牛,马等等。

什么是多态性?

多态性是指在不考虑实例类型的情况下使用实例,多态性分为静态多态性和动态多态性
静态多态性:如任何类型都可以用运算符+进行运算
动态多态性:如下
 import abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def color(self):
        pass

class Dog(Animal):
    def color(self):
        print("Dog is black")

class Cat(Animal):
    def color(self):
        print("Cat is blue")

dog = Dog()
cat = Cat()
dog.color()
cat.color()

# 定义一个统一的接口来访问
def func(object):
    object.color()

func(dog)

执行结果:
Dog is black
Cat is blue
Dog is black

为什么要用多态性?

增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
增加了程序额可扩展性:通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

 import abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def color(self):
        pass

class Dog(Animal):
    def color(self):
        print("Dog is black")

class Cat(Animal):
    def color(self):
        print("Cat is blue")

class Pig(Animal): # 属于动物的另外一种形态:猪
    def color(self):
        print("Pig is white")

dog = Dog()
cat = Cat()
pig = Pig()

# 统一接口,对于使用者来说,自己的代码根本无需改动
def func(object):
    object.color()

# 甚至连调用方式都无需改变,就能调用出pig的talk功能
func(pig)

执行结果:
Pig is white

鸭子类型

在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
 class Duck():
    def speak(self):
        print("Duck speaking like a duck")

    def swim(self):
        print("Duck swim like a duck")

class Person():
    def speak(self):
        print("this people speaking like a duck")

    def swim(self):
        print("this people swim like a duck")

可以很明显的看出,Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了两个方法speak()和swim()。我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有speak()和swim()方法,就可以正确的被调用。
和其它面向对象语言相比,Python不支持方法重载,主要原因是函数功能相同,但是参数类型不同,Python不需要处理,因为 python可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python中很可能是相同的代码,没有必要做成两个不同函数。而函数功能相同,但参数个数不同,python就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

运算符重载

运算符重载是让自定义的类生成的对象(实例)能够使用运算符进行操作
1、算术运算符的重载:

           

           

           

           

           

           

           

方法名 运算符和表达式 说明
__add__(self,rhs) self + rhs 加法
__sub__(self,rhs) self - rhs 减法
__mul__(self,rhs) self * rhs 乘法
__truediv__(self,rhs) self / rhs 除法
__floordiv__(self,rhs) self //rhs 地板除
__mod__(self,rhs) self % rhs 取模(求余)
__pow__(self,rhs) self **rhs 幂运算
具体事例如下:
 class Number():
    def __init__(self,v):
        self.data = v

    def __repr__(self):
        return "Number(%d)"%self.data

    def __add__(self,other):
        v = self.data + other.data
        return Number(v)

    def __sub__(self,other):
        v = self.data - other.data
        return Number(v)

n1 = Number(100)
n2 = Number(200)
n3 = n1 + n2
print(n3)
n4 = n2 - n1
print(n4)
print(n1.__add__(n2))
print(n2.__sub__(n1))

执行结果:
Number(300)
Number(100)
Number(300)
Number(100)

2、反向算术运算符的重载

           

           

           

           

           

           

           

           

方法名 运算符和表达式 说明
__radd__(self,lhs) lhs + self 加法
__rsub__(self,lhs) lhs - self 减法
__rmul__(self,lhs) lhs * self 乘法
__rtruediv__(self,lhs) lhs / self
__rfloordiv__(self,lhs) lhs // self 地板除
__rmod__(self,lhs) lhs % self 取模(求余)
__rpow__(self,lhs) lhs ** self 幂运算
3、复合赋值算术运算符的重载

       

       

       

       

       

       

       

方法名 运算符和表达式 说明
__iadd__(self,rhs) self += rhs 加法
__isub__(self,rhs) self -= rhs 减法
__imul__(self,rhs) self *= rhs 乘法
__itruediv__(self,rhs) self /= rhs 除法
__ifloordiv__(self,rhs) self //=rhs 地板除
__imod__(self,rhs) self %= rhs 取模(求余)
__ipow__(self,rhs) self **=rhs 幂运算

   

4、比较算术运算符的重载

   

       

       

       

       

       

       

方法名 运算符和表达式 说明
__lt__(self,rhs) self < rhs 小于
__le__(self,rhs) self <= rhs 小于等于
__gt__(self,rhs) self > rhs 大于
__ge__(self,rhs) self >= rhs 大于等于
__eq__(self,rhs) self == rhs 等于
__ne__(self,rhs) self != rhs 不等于

            

5、位运算符重载

   

       

       

       

       

       

方法名 运算符和表达式 说明
__and__(self,rhs) self & rhs 位与
__or__(self,rhs) self | rhs 位或
__xor__(self,rhs) self ^ rhs 位异或
__lshift__(self,rhs) self <<rhs 左移
__rshift__(self,rhs) self >>rhs 右移

   

6、反向位运算符重载

             

       

       

       

       

       

方法名 运算符和表达式 说明
__and__(self,lhs) lhs & rhs 位与
__or__(self,lhs) lhs | rhs 位或
__xor__(self,lhs) lhs ^ rhs 位异或
__lshift__(self,lhs) lhs <<rhs 左移
__rshift__(self,lhs) lhs >>rhs 右移

   

7、复合赋值位相关运算符重载

           

       

       

       

       

       

方法名 运算符和表达式 说明
__iand__(self,rhs) self & rhs 位与
__ior__(self,rhs) self | rhs 位或
__ixor__(self,rhs) self ^ rhs 位异或
__ilshift__(self,rhs) self <<rhs 左移
__irshift__(self,rhs) self >>rhs 右移

            

8、一元运算符的重载

     

     

     

     

方法名 运算符和表达式 说明
__neg__(self) - self 负号
__pos__(self) + self 正号
__invert__(self) ~ self 取反
9、索引和切片运算符重载方法

       

       

       

       

方法名 运算符和表达式 说明
__getitem__(self,i) x = self(i) 索引/切片取值
__setitem__(self,i,v) self[i] = v 索引/切片赋值
__delitem__(self,i) del self[i] del语句删除索引/切片