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 enumerate遍历数组示例应用
Sep 06 Python
python实现的阳历转阴历(农历)算法
Apr 25 Python
使用Python的Supervisor进行进程监控以及自动启动
May 29 Python
python字符串排序方法
Aug 29 Python
深入解析Python的Tornado框架中内置的模板引擎
Jul 11 Python
详解python开发环境搭建
Dec 16 Python
Python实现螺旋矩阵的填充算法示例
Dec 28 Python
python numpy和list查询其中某个数的个数及定位方法
Jun 27 Python
替换python字典中的key值方法
Jul 06 Python
Python框架Flask的基本数据库操作方法分析
Jul 13 Python
python利用Tesseract识别验证码的方法示例
Jan 21 Python
Python实现的合并两个有序数组算法示例
Mar 04 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
玩家交还《星际争霸》原始码光盘 暴雪报以厚礼
2017/05/05 星际争霸
PHP日期时间函数的高级应用技巧
2009/05/16 PHP
curl不使用文件存取cookie php使用curl获取cookie示例
2014/01/26 PHP
详解Yii2高级版引入bootstrap.js的一个办法
2017/03/21 PHP
基于PHP+Jquery制作的可编辑的表格的代码
2011/04/10 Javascript
用Javascript评估用户输入密码的强度实现代码
2011/11/30 Javascript
用js小类库获取浏览器的高度和宽度信息
2012/01/15 Javascript
JavaScript中instanceof运算符的使用示例
2016/06/08 Javascript
js控件Kindeditor实现图片自动上传功能
2020/07/20 Javascript
bootstrap datetimepicker实现秒钟选择下拉框
2017/01/05 Javascript
深入理解JavaScript中的for循环
2017/02/07 Javascript
jQuery zTree树插件动态加载实例代码
2017/05/11 jQuery
JavaScript基础教程之如何实现一个简单的promise
2018/09/11 Javascript
JS实现深度优先搜索求解两点间最短路径
2019/01/17 Javascript
说说如何利用 Node.js 代理解决跨域问题
2019/04/22 Javascript
vant 自定义 van-dropdown-item的用法
2020/08/05 Javascript
Python类属性的延迟计算
2016/10/22 Python
Python3 循环语句(for、while、break、range等)
2017/11/20 Python
Python多进程库multiprocessing中进程池Pool类的使用详解
2017/11/24 Python
python使用itchat库实现微信机器人(好友聊天、群聊天)
2018/01/04 Python
Python PyQt4实现QQ抽屉效果
2018/04/20 Python
python 剪切移动文件的实现代码
2018/08/02 Python
Python XlsxWriter模块Chart类用法实例分析
2019/03/11 Python
Python中文分词库jieba,pkusegwg性能准确度比较
2020/02/11 Python
Django实现后台上传并显示图片功能
2020/05/29 Python
pycharm中选中一个单词替换所有重复单词的实现方法
2020/11/17 Python
python 列表推导和生成器表达式的使用
2021/02/01 Python
Perry Ellis官网:美国男士品味服装
2016/12/09 全球购物
中科方德软件测试面试题
2016/04/21 面试题
矫正人员思想汇报
2014/01/08 职场文书
网上卖盒饭创业计划书
2014/01/26 职场文书
领导干部群众路线个人对照检查材料思想汇报
2014/09/30 职场文书
党的群众路线教育实践活动个人整改方案
2014/10/25 职场文书
退休职工欢送会致辞
2015/08/01 职场文书
2016年共产党员个人承诺书
2016/03/24 职场文书
详解Android中的TimePickerView(时间选择器)的用法
2022/04/30 Java/Android