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进阶教程之动态类型详解
Aug 30 Python
解读Django框架中的低层次缓存API
Jul 24 Python
Python制作简单的网页爬虫
Nov 22 Python
Python中内置数据类型list,tuple,dict,set的区别和用法
Dec 14 Python
详解python中xlrd包的安装与处理Excel表格
Dec 16 Python
Python Unittest自动化单元测试框架详解
Apr 04 Python
使用Python对微信好友进行数据分析
Jun 27 Python
python 3.7.0 下pillow安装方法
Aug 27 Python
Python 使用PyQt5 完成选择文件或目录的对话框方法
Jun 27 Python
对于Python深浅拷贝的理解
Jul 29 Python
python有序查找算法 二分法实例解析
Feb 18 Python
Python如何在单元测试中给对象打补丁
Aug 03 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
如何使用PHP对网站验证码进行破解
2015/09/17 PHP
php中实现进程锁与多进程的方法
2016/09/18 PHP
php中static 静态变量和普通变量的区别
2016/12/01 PHP
PHP迭代器和生成器用法实例分析
2019/09/28 PHP
JavaScript 组件之旅(二)编码实现和算法
2009/10/28 Javascript
JS解决url传值出现中文乱码的另类办法
2013/04/08 Javascript
JS远程获取网页源代码实例
2013/09/05 Javascript
SeaJS入门教程系列之SeaJS介绍(一)
2014/03/03 Javascript
js构造函数、索引数组和属性的实现方式和使用
2014/11/16 Javascript
JavaScript日期类型的一些用法介绍
2015/03/02 Javascript
javascript实现的固定位置悬浮窗口实例
2015/04/30 Javascript
JS实现IE状态栏文字缩放效果代码
2015/10/24 Javascript
iScroll.js 使用方法参考
2016/05/16 Javascript
JS判断是否手机或pad访问实现方法
2016/12/09 Javascript
nodejs Assert中equal(),strictEqual(),deepEqual(),strictDeepEqual()比较
2017/09/18 NodeJs
基于vue1和vue2获取dom元素的方法
2018/03/17 Javascript
微信小程序实现自定义picker选择器弹窗内容
2020/05/26 Javascript
浅谈VUE防抖与节流的最佳解决方案(函数式组件)
2019/05/22 Javascript
[01:06:30]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第二场 1月9日
2021/03/11 DOTA
python启动办公软件进程(word、excel、ppt、以及wps的et、wps、wpp)
2009/04/09 Python
用Python调用win命令行提高工作效率的实例
2019/08/14 Python
Python使用matplotlib实现交换式图形显示功能示例
2019/09/06 Python
pytorch forward两个参数实例
2020/01/17 Python
python通过函数名调用函数的几种场景
2020/09/23 Python
Python实现迪杰斯特拉算法并生成最短路径的示例代码
2020/12/01 Python
Python创建简单的神经网络实例讲解
2021/01/04 Python
详解HTML5中rel属性的prefetch预加载功能使用
2016/05/06 HTML / CSS
泰海淘:泰国king Power王权免税集团旗下跨境海淘综合型电商
2020/07/26 全球购物
运动鞋、街头服装、手表和手袋的实时市场:StockX
2020/11/25 全球购物
可以在一个PHP文件里面include另外一个PHP文件两次吗
2015/05/22 面试题
咖啡厅创业计划书范本
2014/01/22 职场文书
个人租房协议书
2014/04/09 职场文书
集中整治工作方案
2014/05/01 职场文书
土建技术员岗位职责
2015/04/11 职场文书
SQLServer2019 数据库的基本使用之图形化界面操作的实现
2021/04/08 SQL Server
Java使用httpRequest+Jsoup爬取红蓝球号码
2021/07/02 Java/Android