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标准算法实现数组全排列的方法
Mar 17 Python
在Python中用get()方法获取字典键值的教程
May 21 Python
Python cookbook(数据结构与算法)找到最大或最小的N个元素实现方法示例
Feb 13 Python
Python基于最小二乘法实现曲线拟合示例
Jun 14 Python
如何在python字符串中输入纯粹的{}
Aug 22 Python
解决nohup执行python程序log文件写入不及时的问题
Jan 14 Python
用python 实现在不确定行数情况下多行输入方法
Jan 28 Python
详解python实现交叉验证法与留出法
Jul 11 Python
Transpose 数组行列转置的限制方式
Feb 11 Python
django-crontab实现服务端的定时任务的示例代码
Feb 17 Python
python3 简单实现组合设计模式
Jul 02 Python
python装饰器实现对异常代码出现进行自动监控的实现方法
Sep 15 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
10个可以简化php开发过程的MySQL工具
2010/04/11 PHP
php下封装较好的数字分页方法
2010/11/23 PHP
PHP中$_SERVER使用说明
2015/07/05 PHP
静态的动态续篇之来点XML
2006/12/23 Javascript
javascript delete 使用示例代码
2010/03/29 Javascript
JavaScript中实现PHP的打乱数组函数shuffle实例
2014/10/11 Javascript
js实现对table动态添加、删除和更新的方法
2015/02/10 Javascript
JavaScript实现级联菜单的方法
2015/06/29 Javascript
JS动态改变浏览器标题的方法
2016/04/06 Javascript
Easyui 之 Treegrid 笔记
2016/04/29 Javascript
微信小程序开发探究
2016/12/27 Javascript
用jQuery实现可输入多选下拉组合框实例代码
2017/01/18 Javascript
jquery ajaxfileupload异步上传插件使用详解
2017/02/08 Javascript
jQuery遍历节点方法汇总(推荐)
2017/05/13 jQuery
详解微信小程序与内嵌网页交互实现支付功能
2018/10/22 Javascript
关于Vue源码vm.$watch()内部原理详解
2019/04/26 Javascript
使用Vue实现移动端左滑删除效果附源码
2019/05/16 Javascript
使用layui日期控件laydate对开始和结束时间进行联动控制的方法
2019/09/06 Javascript
jQuery+Ajax+js实现请求json格式数据并渲染到html页面操作示例
2020/06/02 jQuery
vue商城中商品“筛选器”功能的实现代码
2020/07/01 Javascript
JavaScript this指向相关原理及实例解析
2020/07/10 Javascript
Python continue语句用法实例
2014/03/11 Python
深入了解Python中pop和remove的使用方法
2018/01/09 Python
Python通过属性手段实现只允许调用一次的示例讲解
2018/04/21 Python
日本乐天官方海外转运服务:Rakuten Global Express
2018/11/30 全球购物
局域网标准
2016/09/10 面试题
研究生毕业鉴定
2014/01/29 职场文书
甜美蛋糕店创业计划书
2014/01/30 职场文书
大学毕业生求职自荐信
2014/02/20 职场文书
公司承诺书怎么写
2014/05/24 职场文书
党员四风自我剖析材料
2014/10/07 职场文书
2014年信息中心工作总结
2014/12/17 职场文书
辞职书格式样本
2015/02/26 职场文书
初中团支书竞选稿
2015/11/21 职场文书
2016年幼儿园万圣节活动总结
2016/04/05 职场文书
火锅店的开业营销方案范本!
2019/07/05 职场文书