Python面向对象程序设计类的多态用法详解


Posted in Python onApril 12, 2019

本文实例讲述了Python面向对象程序设计类的多态用法。分享给大家供大家参考,具体如下:

多态

1、多态使用

一种事物的多种体现形式,举例:动物有很多种

注意: 继承是多态的前提

函数重写就是多态的体现形式

演示:重写Animal类

第一步:先定义猫类和老鼠类,继承自object,在其中书写构造方法和eat方法
第二步: 抽取Animal父类,定义属性和eat方法,猫类与老鼠类继承即可
第三步: 定义人类,在其中分别定义喂猫和喂老鼠的方法
第四步:使用多态,将多个喂的方法提取一个。

# 测试类
from cat import Cat
from mouse import Mouse
from person import Person
'''
多态: 一种事物的多种状态
需求:人可以喂任何一种动物
'''
#创建猫和老鼠的对象
tom = Cat("tom")
jerry = Mouse("jerry")
#调用各自的方法
tom.eat()
jerry.eat()
#定义了一个有name属性和eat方法的Animal类,让所有的动物类都继承自Animal.
#定义一个人类,可以喂猫和老鼠吃东西
per = Person()
#per.feedCat(tom)
#per.feedMouse(jerry)
#思考:人要喂100种动物,难道要写100个feed方法吗?
#前提:tom和jerry都继承自动物
per.feedAnimal(tom)
per.feedAnimal(jerry)

输出:

tom吃
jerry吃
给你食物
tom吃
给你食物
jerry吃

#animal.py文件中的动物类
class Animal(object):
  def __init__(self, name):
    self.name = name
  def eat(self):
    print(self.name + "吃")
#cat.py文件中的猫类
class Cat(Animal):
  def __init__(self, name):
    #self.name = name
    super(Cat,self).__init__(name)
#mouse.py中的老鼠类
class Mouse(Animal):
  def __init__(self, name):
    #self.name = name
    super(Mouse,self).__init__(name)
#person.py中的人类
class Person(object):
  def feedAnimal(self, ani):
    print("给你食物")
    ani.eat()

2、对象属性与类属性

对象属性和类属性的区别:

a.定义的位置不同,类属性是直接在类中的属性,对象属性是在定义在构造方法中的属性;

b.对象属性使用对象访问,类属性使用类名访问;

c.在内存中出现的时机不同[类属性随着类的加载而出现,对象属性随着对象的创建而出现];

d.优先级不同,对象属性的优先级高于类属性。

class Person(object):
  #1.定义位置
  #类属性:直接定义在类中的属性
  name = "person"
  def __init__(self, name):
    #对象属性:定义在构造方法中的属性
    self.name = name
#2.访问方式
print(Person.name)
per = Person("tom")
#对象属性的优先级高于类属性
print(per.name)
#动态的给对象添加对象属性
per.age = 18
#只针对当前对象生效,对于类创建的其他对象没有作用
print(Person.name)
per2 = Person("lilei")
#print(per2.age) #没有age属性
#删除对象中的name属性,再调用会使用到同名的类属性
del per.name
print(per.name)
#注意事项:不要将对象属性与类属性重名,因为对象属性会屏蔽掉类属性,但是当删除对象属性之后,再使用就能使用到类属性了.

输出:

person
tom
person
person

3、动态添加属性和方法

正常情况下,我们定义了一个class,创建一个class的实例后,我们可以给该实例绑定任何的的属性和方法,这就是动态语言的灵活性。

python语言的特点:灵活。

这里说的动态添加属性和方法主要指的是关于slots函数的使用

from types import MethodType
#定义一个空类
'''
class Person():
  pass
'''
class Person(object):
  __slots__ = ("name","age","speak","hobby")
  pass
# 动态添加属性[体现了动态语言的特点:灵活性]
per = Person()
per.name = "tom"
print(per.name)
#动态添加方法
def say(self):
  print("my name is "+ self.name)
per.speak = say
per.speak(per)
#这样实现不好,所以引入MethodType
def hobby(self):
  print("my hobby is running")
per.hobby = MethodType(hobby,per)
per.hobby()

输出:

tom
my name is tom
my hobby is running

但是,给一个实例绑定的方法对另外一个实例是不起作用的。

为了给所有的实例都绑定方法,可以通过给class绑定方法

#动态添加方法
def say(self,name):
  self.name = name
  print("my name is "+ self.name)
Person.speak = say
per2 = Person()
per2.speak('hh')

输出:

my name is hh

给class绑定方法后,所有的实例均可调用。

4、slots

通常情况下,上面的say方法可以直接定义在class中,但动态绑定允许我们在程序在运行的过程中动态的给class添加功能,这在静态语言中很难实现。

如果我们想限制实例的属性怎么办?

比如,只允许给Person实例添加name,age属性,为了达到限制的目的,Python中允许在定义class的时候,定义一个特殊的变量【slots】变量,来限制该class添加的属性

class Person(object):
  __slots__=("name","age")
#[不想无限制的任意添加属性]
#比如,只允许给对象添加name, age属性
#解决:定义类的时候,定义一个特殊的属性(__slots__),可以限制动态添加的属性范围
per = Person()
per.height = 170
print(per.height)

这样做会报错

AttributeError: 'Person' object has no attribute 'height'

使用slots的时候需要注意,slots定义的属性仅仅对当前类的实例起作用,对继承的子类是不起作用的。

除非在子类中也定义slots,这样子类实例允许定义的属性就是自身的slots加上父类的slots。

总结:

__slots__:

语法:

__slots__ = (属性名1,属性名2,...)

作用:

限制类的属性名

注意:当子类没有添加slots时,子类继承父类的时候,它的属性名不受父类的影响

若子类中也添加slots,子类的限制应该是父类的slots与子类slots的并集

5、@property

绑定属性时,如果我们直接把属性暴露出去,虽然写起来简单,但是没有办法检查参数,导致可以随意的更改。

比如:

p = Person()
p.age = -1

这显然不合常理,为了限制age的范围,我们可以通过setAge()的方法来设置age,再通过getAge()的方法获取age,这样在setAge()中就可以检查输入的参数的合理性了。

class Person(object):
  def __init__(self, name, age):
    # 属性直接对外暴露
    # self.age = age
    # 限制访问
    self.__age = age
    self.__name = name
    # self.__name = name
  def getAge(self):
    return self.__age
  def setAge(self, age):
    if age < 0:
      age = 0
    self.__age = age
  # 通过@property和@age.setter改变原来的get/set方法
  # 方法名为受限制的变量去掉双下划线
  # 相当于get方法
  @property
  def age(self):
    return self.__age
  # 相当于set的方法
  @age.setter # 去掉下划线.setter
  def age(self, age):
    if age < 0:
      age = 0
    self.__age = age
  @property
  def name(self):
    return self.__name
  @name.setter
  def name(self, name):
    self.__name = name
per = Person("lili", 18)
# 属性直接对外暴露
# 不安全,没有数据的过滤
# per.age = -10
# print(per.age)
# 使用限制访问,需要自己写set和get的方法才能访问
# 劣势:麻烦,代码不直观
# 思考问题:如果我就想使用对象"."的方式访问对象的私有属性,怎么办?
# per.setAge(15)
# print(per.getAge())
# property:可以让你对受限制访问的属性使用"."语法
per.age = 80 # 相当于调用setAge
print(per.age) # 相当于调用getAge
print(per.name)

输出:

80
lili

property

总结语法:

针对私有化的属性添加的。

@property
  def 属性名(self):
    return self.__属性名
@属性名.setter
  def 属性名(self, 值):
    #业务逻辑处理
    self.属性名 = 值

总结:

a.装饰器(decorator)可以给函数动态加上功能,对于类的方法,装饰器一样起作用,python内置的@property装饰器就是负责把一个方法变成属性调用的。
b.@property的实现比较复杂,我们先考虑如何使用,把一个getter方法变成属性,只需要加上@property就可以了,此时@property本身又创建了另一个装饰器@属性setter,负责把一个setter方法变成属性赋值.
c.@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

6、运算符重载

类可以重载加减运算,打印,函数调用,索引等内置运算,运算符重载使我们的对象的行为与内置函数一样,在python调用时操作符会自动的作用于类的对象,python会自动的搜索并调用对象中指定的方法完成操作。

1、常见运算符重载方法

常见运算符重载方法

方法名 重载说明 运算符调用方式
init 构造函数 对象创建: X = Class(args)
del 析构函数 X对象收回
add sub 加减运算 X+Y, X+=Y/X-Y, X-=Y
or 运算符| X|Y, X|=Y
str_ repr 打印/转换 print(X)、repr(X)/str(X)
call 函数调用 X(*args, **kwargs)
getattr 属性引用 X.undefined
setattr 属性赋值 X.any=value
delattr 属性删除 del X.any
getattribute 属性获取 X.any
getitem 索引运算 X[key],X[i:j]
setitem 索引赋值 X[key],X[i:j]=sequence
delitem 索引和分片删除 del X[key],del X[i:j]
len 长度 len(X)
bool 布尔测试 bool(X)
lt gt le ge eq ne 特定的比较 依次为XY,X=Y, X==Y,X!=Y 注释:(lt: less than, gt: greater than, le: less equal, ge: greater equal, eq: equal, ne: not equal )
radd 右侧加法 other+X
iadd 实地(增强的)加法 X+=Y(or else add)
iter next 迭代 I=iter(X), next()
contains 成员关系测试 item in X(X为任何可迭代对象)
index 整数值 hex(X), bin(X), oct(X)
enter exit 环境管理器 with obj as var:
get set delete 描述符属性 X.attr, X.attr=value, del X.attr
new 创建 在init之前创建对象
# 举例
# 数字和字符串都能相加
#print(1 + 2)
#print("1" + "2")
# 不同的类型用加法会有不同的解释
class Person(object):
  def __init__(self, num):
    self.num = num
  # 运算符重载
  def __add__(self, other):
    return Person(self.num + other.num)
  # 方法重写
  def __str__(self):
    return "num = " + str(self.num)
# 如果两个对象相加会怎样?
# 对象相加,编译器解释不了,所以就要用到运算符重载
per1 = Person(1)
per2 = Person(2)
print(per1 + per2)
# 结果为地址:per1+per2 === per1.__add__(per2),如果想得到num的和则重写str方法
# 上述打印就等价于:print(per1.__add__(per2)),只不过add方法会自动调用
print(per1)
print(per2)

输出:

num = 3
num = 1
num = 2

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python实现批量监控网站
Sep 09 Python
PyCharm在win10的64位系统安装实例
Nov 26 Python
Python中的上下文管理器和with语句的使用
Apr 17 Python
python模块导入的细节详解
Dec 10 Python
Python3 串口接收与发送16进制数据包的实例
Jun 12 Python
详解pandas数据合并与重塑(pd.concat篇)
Jul 09 Python
Python3视频转字符动画的实例代码
Aug 29 Python
django商品分类及商品数据建模实例详解
Jan 03 Python
Python类的动态绑定实现原理
Mar 21 Python
python3 sqlite3限制条件查询的操作
Apr 07 Python
python3.7.2 tkinter entry框限定输入数字的操作
May 22 Python
python神经网络Xception模型
May 06 Python
Python中format()格式输出全解
Apr 12 #Python
Python面向对象程序设计类的封装与继承用法示例
Apr 12 #Python
详解python3 + Scrapy爬虫学习之创建项目
Apr 12 #Python
Python2和Python3的共存和切换使用
Apr 12 #Python
Python面向对象程序设计类变量与成员变量、类方法与成员方法用法分析
Apr 12 #Python
Python、 Pycharm、Django安装详细教程(图文)
Apr 12 #Python
Python面向对象程序设计构造函数和析构函数用法分析
Apr 12 #Python
You might like
CI框架中数据库操作函数$this-&gt;db-&gt;where()相关用法总结
2016/05/17 PHP
详解PHP防止盗链防止迅雷下载的方法
2017/04/26 PHP
Nigma vs AM BO3 第一场2.13
2021/03/10 DOTA
ASP.NET jQuery 实例16 通过控件CustomValidator验证RadioButtonList
2012/02/03 Javascript
js动态创建标签示例代码
2014/06/09 Javascript
理运用命名空间让js不产生冲突避免全局变量的泛滥
2014/06/15 Javascript
JavaScript构造函数详解
2015/12/27 Javascript
JavaScript Date对象详解
2016/03/01 Javascript
Javascript的表单验证-提交表单
2016/03/18 Javascript
深入理解JS addLoadEvent函数
2016/05/20 Javascript
JS简单随机数生成方法
2016/09/05 Javascript
使用PBFunc在Powerbuilder中支付宝当面付款功能
2016/10/01 Javascript
JavaScript日期选择功能示例
2017/01/16 Javascript
vue实现验证码输入框组件
2017/12/14 Javascript
Angular2.0实现modal对话框的方法示例
2018/02/18 Javascript
React Hooks的深入理解与使用
2018/11/12 Javascript
vue单页应用的内存泄露定位和修复问题小结
2019/08/02 Javascript
详解微信小程序图片地扯转base64解决方案
2019/08/18 Javascript
[02:19]2014DOTA2国际邀请赛 专访820少年们一起去追梦吧
2014/07/14 DOTA
[01:14]英雄,所敬略同——2018完美盛典宣传视频4K
2018/12/05 DOTA
[41:12]Liquid vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.24
2019/09/10 DOTA
python编写暴力破解FTP密码小工具
2014/11/19 Python
json跨域调用python的方法详解
2017/01/11 Python
详解python之简单主机批量管理工具
2017/01/27 Python
利用Python实现网络测试的脚本分享
2017/05/26 Python
Python网络爬虫中的同步与异步示例详解
2018/02/03 Python
python如何压缩新文件到已有ZIP文件
2018/03/14 Python
python将每个单词按空格分开并保存到文件中
2018/03/19 Python
Django 开发环境配置过程详解
2019/07/18 Python
Django使用模板后无法找到静态资源文件问题解决
2019/07/19 Python
Django框架请求生命周期实现原理
2020/11/13 Python
GWT都有什么特性
2016/12/02 面试题
法学研究生自我鉴定范文
2013/12/04 职场文书
精通CAD能手自荐书
2014/01/31 职场文书
个人批评与自我批评发言稿
2014/09/28 职场文书
毕业欢送会致辞
2015/07/29 职场文书