在学面向对象时,归一化程序设计,抽象类,接口类,多态,鸭子类型这些总会让人感到困扰,这里详细写下以方便复习.
归一化程序设计
我们都知道len()方法可以得出几乎所有数据类型的长度,那他究竟是什么原理呢?
其实他是一个__len__()的方法来完成的.如Hello world的长度
s = 'hello world'
print(s.__len__())
11
大部分数据类型都可以用__len__()方法
list.__len__() # 列表
tuple.__len__() # 元组
set.__len__() # 集合
dict.__len__() # 字典
这里引出归一化设计:
def my_len(obj): # 归一化设计
return obj.__len__()
print(my_len('abc'))
# 无论传过来的参数是什么数据类型
# 我都可以帮助你调用这个类中的__len__方法,并将结果返回给你
归一化设计 : 从原来的面向对象编程 -->面向函数编程.降低了用户的使用成本
抽象类
这里我们先定义两个类,两个类都有一个吃饭方法.
class Dog:
def __init__(self,name):
self.name = name
def bash(self):
print('%s is bashing'%self.name)
def eat(self):
print('%s is eating'%self.name)
class Cat:
def __init__(self,name):
self.name = name
def yell(self):
print('%s is yelling'%self.name)
def eat(self):
print('%s is eating'%self.name)
对于猫和狗来说,他们吃饭的方法是一样的,我们可以从这些类当中抽象出一个类,里头有一个吃饭方法,并让其他有这个方法的类去继承这个类.
我们导入ABCMeta,abstractmethod模块,这是一个抽象的方法
from abc import ABCMeta,abstractmethod
class Animal(metaclass=ABCMeta): # metaclass 元类 metaclass = ABCMeta表示Payment类是一个规范类
@abstractmethod # @abstractmethod表示下面一行中的eat方法是一个必须在子类中实现的方法
def eat(self):pass
class Dog(Animal):
def __init__(self,name):
self.name = name
def bash(self):
print('%s is bashing'%self.name)
def eat(self):
print('%s is eating'%self.name)
class Cat(Animal):
def __init__(self,name):
self.name = name
def yell(self):
print('%s is yelling'%self.name)
def eat(self):
print('%s is eating'%self.name)
抽象类主要就是作为基类/父类,来约束子类中必须实现的某些方法
抽象类的特点:
- 必须在类定义的时候指定metaclass = ABCMeta
- 必须在要约束的方法上方加上@abstractmethod方法
这里提一个重点: 抽象类不能实例化
接口类
在说接口类之前,先提一下python和java两种语言之间多继承的区别:
- java 不支持多继承
- python 支持多继承 :通过抽象类的多继承来实现复杂的规范
再举一个例子,动物园里有三个类,天鹅,狮子,鹦鹉,天鹅会飞,走,游泳,狮子会走和游泳,而鹦鹉会飞会走还会说话.
但是不同动物的相同行为是不能混为一谈,不同动物却有相同行为
这里我们写飞动物,游泳动物,走动物三个类并分别在里头写飞,游泳,走方法,让三个动物类分别继承他
from abc import ABCMeta,abstractmethod
class Fly_Animal(metaclass=ABCMeta):
@abstractmethod
def fly(self):
print('爷会飞')
class Swim_Animal(metaclass=ABCMeta):
@abstractmethod
def swim(self): pass
class Walk_Animal(metaclass=ABCMeta):
@abstractmethod
def walk(self): pass
class Swan(Fly_Animal,Swim_Animal,Walk_Animal):
def fly(self):
super().fly()
print('飞')
def walk(self):print('走')
def swim(self):print('游')
class Lionr(Walk_Animal,Swim_Animal):
def walk(self):print('走')
def swim(self):print('游')
class Parrot(Fly_Animal,Walk_Animal):
def fly(self):print('飞')
def walk(self):print('走')
def talk(self):print('说')
接口类更接近java中的接口的概念
而python中由于有了抽象类的多继承,不需要接口的概念了
接口类其实就是一个基类写出来被子类多继承
多态
python中处处是多态,他是一个自带多态的语言.
多态是同一个行为具有多个不同表现形式或形态的能力
我们举一个简单的例子,我们比方说一个软件,他有普通用户,普通用户之下又有vip,svip,vvip各种用户形式,vip,svip,vvip都是基于普通用户而出现的,服务于普通用户,这里我们将vip,svip等这些普通用户所演化的多种形态称为多态.
再比如说,支付表现出了多种状态:
- 支付宝
- 微信
- apple pay
class Payment(object):pass
class Alipay(Payment):
def pay(self,money):
pass
class Wechatpay(Payment):
def pay(self,money):
pass
class Applepay(Payment):
def pay(self,money):
pass
def pay(person_obj,money):
person_obj.pay(money)
wang = Alipay()
pay(wang,200)
quan = Wechatpay()
pay(quan,300)
不同的类,做相同(方法)的调用,产生不同的结果,这里表现出来的一个payment类表现出来的多种支付状态就称为多态,且不同于静态语言(例如Java),python(动态语言)调用实例方法,不检查类型,只要给了参数且方法存在且正确,就可以调用.
借用一句话,多态用一句话概括就是,有这种方法,并且传入相应的参数
鸭子类型
来讲讲起源,Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-1916)的诗句:”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.” 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。这个概念有点让人头晕,我简单说.
在鸭子类型中,我们关注的不是对象类型的本身,而是他是如何使用的.这里我们再讲len这个方法,我们已经知道了len这个函数的内部机制,然而对于len()函数来说,无论你给的是list,str,dict或者set这些类型,他都不会关注,而你只要是能计算出长度的参数(能用__len__方法的),我(len()函数)就接受.抽象的说,数据集合就是鸭子,而list,str,dict等这些就是鸭子类型(具有数据集合的所有特性,但却不能说他就是数据集合,他表现的像是一个数据集合,一个能计算出长度的数据集合).
总结
说了这么多,这些其实都是一个中心思想:不同类之间的相同的方法都应该用同一个名字.
稍微分辨一下就是归一化设计是为了使用不同类中的方法,而要求 不同类之间的相同的方法都应该用同一个名字
而多态是已经实现了"不同类之间的相同的方法都用同一个名字",解决不同类的对象的传参问题
最后是鸭子类型是不需要解决类的对象的传参问题,就可以直接利用已经实现的"不同类之间的相同的方法都用同一个名字"