面向对象三大特性之多态
面向对象三大特性之多态
1.什么叫多态
多态指的是同一种类型的事物,不同的形态。
class Animal: # 将方法统一成父类的call
def call(self):
pass
class Pig(Animal):
def call(self):
print('哼哧哼哧')
class Dog(Animal):
def call(self):
print('汪汪汪')
class Cat(Animal):
def call(self):
print('喵喵喵')
实例化得到三个对象
pig_obj = Pig()
dog_obj = Dog()
cat_obj = Cat()
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是动物,但凡是动物肯定有call方法,于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用
cat_obj.call()
pig_obj.call()
dog_obj.call()
输出结果:
喵喵喵
哼哧哼哧
汪汪汪
2.多态的目的
“多态” 也称之为 “多态性”,目的是为了 在不知道对象具体类型的情况下,统一对象调用方法的规范
多态的表现 “形式之一” 就是继承:
- 先抽象,再继承
父类: 定制一套统一的规范。(比如: 方法名统一)
子类: 遵循父类的统一的规范。(比如: 子类遵循父类方法名的统一)
综上我们得知,多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
注意: 在python中不会强制限制 子类 必须要遵循 父类 的规范,所以出现了抽象类
3.什么是抽象类
在python内置的abc模块中,有一个抽象类(ABCMeta).
4.抽象类的作用:
让子类必须遵循父类的编写规范。
5.如何实现抽象类
- 先导入abc模块,父类需要继承abc模块,在父类名添加括号,括号内写上(metaclass=abc.ABCMeta)
- 在父类的方法中,需要装饰上 abc.abstractmethod
注意: 如果使用抽象类,子类必须按照父类的方法编写规范,缺一不可。(只要父类中有几个抽象方法,子类就必须要定义几个)'''
注意: 在python中不推荐使用抽象类。
import abc
class Animal(metaclass=abc.ABCMeta): # 注意
# @abc.abstractclassmethod # 会有一条线在上面,表示虽然可以用,但是正在被弃用
@abc.abstractmethod # 父类中规范的方法装饰上abstractmethod
def call(self):
pass
class Pig(Animal):
def call(self):
print('哼哧哼哧')
class Dog(Animal):
def tell(self): # 改成其他与父类规定的方法不同就会被报错
print('汪汪汪')
class Cat(Animal):
def call(self):
print('喵喵喵')
pig_obj = Pig()
dog_obj = Dog()
cat_obj = Cat()
cat_obj.call()
pig_obj.call()
dog_obj.tell()
# TypeError: Can't instantiate abstract class Dog with abstract methods call
6.什么是鸭子类型
我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing) 不同的对象,只要长得像鸭子,动作行为像鸭子,那它就是鸭子!
鸭子类型是多态的一种表现形式
7.为什么要有鸭子类型?
不同对象,先抽象出相同类型的方法,给他们定制一套统一的规范。
所有的类,在定义时都按照统一的规范进行编写。
多态的三种表现形式:
-
继承父类
耦合度高,程序的可扩展性低
-
继承抽象类
耦合度极高,程序的可扩展性极低
-
鸭子类型:
耦合度低,程序的可扩展性高
注意: 在python中,强烈推荐使用鸭子类型。'''
class Pig:
def call(self):
print('哼哧哼哧')
class Dog:
def call(self):
print('汪汪汪')
class Cat:
def call(self):
print('喵喵喵')
pig_obj = Pig()
dog_obj = Dog()
cat_obj = Cat()
cat_obj.call()
pig_obj.call()
dog_obj.call()
8.classmethod与staticmethod
classmethod与staticmethod都是python解释器内置的装饰器。
classmethod: 是一个装饰器,给在类内部定义方法中装饰,将类内部的方法变为 “类的绑定方法”。
class DB:
__date = 'tnank is very handsome!!!'
def __init__(self, user, pwd, role):
self.user = user
self.pwd = pwd
self.role = role
# 类绑定方式
@classmethod
def check_db(cls, user, pwd, role):
# 在类方法内部调用类产生一个实例 ---》 对象
obj = cls(user, pwd, role)
if obj.user == 'tank' and obj.pwd == '123' and obj.role == 'admin':
print('检验通过')
print(cls.__date)
return cls.__date
# 对象绑定方式
# def check_db(self, user, pwd, role):
# # obj = self(user, pwd, role)
# if self.user == 'tank' and self.pwd == '123' and self.role == 'admin':
# print('检验通过')
# print(self.__date)
# return self.__date
# 类的绑定方式
DB.check_db('tank', '123', 'admin')
# 对象绑定方式
# db_obj = DB('tank', '123', 'admin')
# db_obj.check_db('tank', '123', 'admin')
# # __class__"查看当前对象的类
# print(db_obj.__class__)
staticmethod: 翻译: 静态方法 是一个装饰器,给在类内部定义方法中装饰,将类内部的方法变为 “非绑定方法”。
-
对象的绑定方法:
由对象来调用,由谁来调用,会将谁(对象)当做第一个参数传入。
class Foo: 对象绑定方法 def func(self): print(self) # 对象绑定方法 obj = Foo() obj.func() # 会将对象当成第一个参数传给self
-
类的绑定方法:
由类来调用,由谁来调用,会将谁(类)当做第一个参数传入。
class Foo:
@classmethod
def func(self):
print(self)
#类绑定方法
Foo.func()
# 会将类当成第一个参数传给self
- 非绑定方法:
可以由对象或类来调用,谁来调用都是一个普通方法(普通函数),方法需要传入几个参数,就得传入几个。
class Foo:
# 非绑定方法
@staticmethod
def func(self):
print(self)
# 非绑定方法
Foo.func(12)# 可以通过类调用,但是必须得传参,需要几个传几个
obj = Foo()# 也可以通过对象调用,也是必须得传参,需要几个传几个
obj.func(12)
9.isinstance与issubclass
isinstance和issubclass是python的内置模块
-
isinstance: 判断一个对象是否是另一个类的实例。
如果是: True
如果不是: False
-
issubclass: 判断一个类是否是另一个类的子类。
如果是: True
如果不是: False
#isinstance:
class Foo:
pass
class Boo:
pass
foo_obj = Foo()
boo_obj = Boo()
# 前一个参数是对象名,后一个参数是类名
print(isinstance(foo_obj, Foo)) # True
print(isinstance(boo_obj, Foo)) # False
# issubclass
class Father:
pass
class Sub(Father):
pass
class Foo:
pass
# 前一个参数是字类名,后一个参数是父类名
print(issubclass(Sub, Father)) # True
print(issubclass(Foo, Father)) # False
10.反射
反射: 反射指的是通过 “字符串” 对 对象的属性进行操作。
- hasattr: 通过 “字符串” 判断对象的属性或方法是否存在。
- getattr: 通过 “字符串” 获取对象的属性或方法。
- setattr: 通过 “字符串” 设置对象的属性或方法。
- delattr: 通过 “字符串” 删除对象的属性或方法。
注意: 反射的四个方法是python内置的。
class Foo:
def __init__(self, x, y):
self.x = x
self.y = y
foo_obj = Foo(10, 20)
#hasattr
#通过字符串x 判断对象中是否有 x属性
print(hasattr(foo_obj, 'x')) # True
print(hasattr(foo_obj, 'y')) # True
print(hasattr(foo_obj, 'z')) # False
#getattr
res = getattr(foo_obj, 'x')
print(res) # 10
#若属性不存在,则返回默认值
res = getattr(foo_obj, 'z', '默认值') # 默认值可以设置
print(res) # 默认值
#setattr
#为foo_obj设置一个属性z,值为30
setattr(foo_obj, 'z', 30)
print(hasattr(foo_obj, 'z')) # True
#delattr
delattr(foo_obj, 'x')
print(hasattr(foo_obj, 'x')) # False
# 反射应用:
class FileControl:
def run(self):
while True:
# 让用户输入上传或下载功能的命令:
user_input = input('请输入 上传(upload) 或 下载(download) 功能:').strip()
# 通过用户输入的字符串判断方法是否存在,然后调用相应的方法
if hasattr(self, user_input):
func = getattr(self, user_input)
func()
else:
print('输入有误!')
def upload(self):
print('文件正在上传...')
def download(self):
print('文件正在下载...')
file_control_obj = FileControl()
file_control_obj.run()
import uuid # 用于产生随机字符串的模块
由时间戳以及某种算法组合而成,会产生一串世界上独一无二字符串。
print(uuid.uuid4()) # 一般选择uuid4这个方法
输出结果:5cb5a5e6-9b6f-4f86-9f70-3443001b9525
原文链接:http://www.cnblogs.com/dkblog/archive/2011/10/10/2205200.html
Python官方Doc:《20.15. uuid — UUID objects according to RFC 4122》
UUID的算法介绍:《A Universally Unique IDentifier (UUID) URN Namespace》
概述:
UUID是128位的全局唯一标识符,通常由32字节的字符串表示。
它可以保证时间和空间的唯一性,也称为GUID,全称为:
UUID —— Universally Unique IDentifier Python 中叫 UUID
GUID —— Globally Unique IDentifier C# 中叫 GUID
它通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生成ID的唯一性。
UUID主要有五个算法,也就是五种方法来实现:
1、uuid1()——基于时间戳
由MAC地址、当前时间戳、随机数生成。可以保证全球范围内的唯一性,
但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC。
2、uuid2()——基于分布式计算环境DCE(Python中没有这个函数)
算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。
实际中很少用到该方法。
3、uuid3()——基于名字的MD5散列值
通过计算名字和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,
和不同命名空间的唯一性,但同一命名空间的同一名字生成相同的uuid。
4、uuid4()——基于随机数
由伪随机数得到,有一定的重复概率,该概率可以计算出来。
5、uuid5()——基于名字的SHA-1散列值
算法与uuid3相同,不同的是使用 Secure Hash Algorithm 1 算法
使用方面:
首先,Python中没有基于DCE的,所以uuid2可以忽略;
其次,uuid4存在概率性重复,由无映射性,最好不用;
再次,若在Global的分布式计算环境下,最好用uuid1;
最后,若有名字的唯一性要求,最好用uuid3或uuid5。
编码方法:
# -*- coding: utf-8 -*-
import uuid
name = "test_name"
namespace = "test_namespace"
print uuid.uuid1() # 带参的方法参见Python Doc
print uuid.uuid3(namespace, name)
print uuid.uuid4()
print uuid.uuid5(namespace, name)