Python中面向对象你应该知道的一下知识


Posted in Python onJuly 10, 2019

0x00 is与==

==运算符是比较两个对象的内容是否相等,默认情况是调用对象的__eq__方法进行比较;而is是比较两个对象是否一样,它比较的两个对象的id,即它们的内存地址是否相同。

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True
# a和b是否是同一个对象
>>> a is b
False
# a和b的地址其实是不一样的
>>> id(a)
4498717128
>>> id(b)
4446861832

在比较时但也有例外。Python对一些常用的值进行缓存优化,例如在区间[-5,256]的整数,它们在创建时,无论创建多少个对象,它们的id是一样的,即它们在底层中只保存一份内存。

>>> a = -5
>>> b = -5
>>> a == b
True
>>> a is b
True
>>> a = -6
>>> b = -6
>>> a == b
True
>>> a is b
False

对一些短的字符串也是如此,因此并不是所有字符串都会创建新的实例

>>> a='123'
>>> b='123'
>>> a==b
True
>>> a is b
True
>>> id(a)
4446903800
>>> id(b)
4446903800
>>> x = 'long char'
>>> y = 'long char'
>>> x == y
True
>>> x is y
False

0x01 __repr__与__str__

每个类都应该提供一个__repr__方法。__repr__方法和__str__方法有什么不一样呢?
简单的说,__repr__可以反映一个对象的类型以及包含的内容,而__str__主要是用于打印一个对象的内容。例如看一下Python中的日期类datetime

import datetime
>>> today = datetime.date.today()
>>> today
datetime.date(2019, 7, 7)
>>> print(today)
2019-07-07
>>> str(today)
'2019-07-07'
>>> repr(today)
'datetime.date(2019, 7, 7)'

__str__在字符串连接,打印等操作会用到,而__repr__主要是面向开发者,它能反馈的信息比较多,例如在交互环境下输入today这个变量会打印出datetime.date(2019, 7, 7),不仅可以看出today代表的是今天的日期信息,还可以看出它的类型信息。更重要的是你可以直接复制这段打印出来的信息,直接构造一个“相同”的对象出来。

例如

>>> now = datetime.date(2019, 7, 7)
>>> now
datetime.date(2019, 7, 7)

0x02 对象复制

对象的复制或说对象拷贝可以分为浅拷贝和深拷贝。

浅拷贝与深拷贝

我们通过代码来说明,就很好理解

如果要拷贝的对象是基本数据类型,那么深拷贝和浅拷贝的区别不是很大。

>>> a = [1,2,3]
>>> b = list(a)
>>> a[1]=200
>>> a
[1, 200, 3]
>>> b
[1, 2, 3]

修改a中的元素并不会影响到b

但如果要拷贝的对象包含了另一个对象,那么就要考虑深拷贝和浅拷贝的问题了。

>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b = list(a)
>>> a == b
True
>>> a is b
False

这里有一个列表a,里面有三个子列表,即列表里包含的是对象。

我们使用list工厂方法创建了一个a的拷贝b,这个b就是a的浅拷贝,为什么呢?

>>> a[1][2]='x'
>>> a
[[1, 2, 3], [4, 5, 'x'], [7, 8, 9]]
>>> b
[[1, 2, 3], [4, 5, 'x'], [7, 8, 9]]

把a[1][2]的元素修改成了x,这时候b列表中也响应了相同的修改。所以这是浅拷贝,因为没有把子对象进行拷贝,只是拷贝了指向子对象的引用。

知道浅拷贝,那么深拷贝就很好理解了。执行拷贝之后,拷贝对象和原对象是完全独立的,修改任何一个对象都不会影响到另一个对象

如何深拷贝一个对象

这时候就需要copy模块了,该模块有两个重要的方法deepcopy和copy。不错,就是分别代表深拷贝和浅拷贝。

>>> import copy
>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b = copy.deepcopy(a)
>>> a
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> b
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> a[1][2]='change'
>>> a
[[1, 2, 3], [4, 5, 'change'], [7, 8, 9]]
>>> b
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

执行深拷贝之后,对a的修改并不会影响到b。

0x03 Abstract Base Classes(ABC)

抽象基类的使用

为了说明为什么要使用ABC,我们先看下不使用ABC的情况

# 定义了基类Base
>>> class Base:
	def foo(self):
		raise NotImplemented
	def bar(self):
		raise NotImplemented

# 定义实现类
>>> class Concrete(Base):
	def foo(self):
		print('called foo')
  
  # 实现类并没有实现bar方法
	
>>> c = Concrete()
>>> c.foo()
called foo
>>> c.bar()
Traceback (most recent call last):
 File "<pyshell#391>", line 1, in <module>
  c.bar()
 File "<pyshell#384>", line 5, in bar
  raise NotImplemented
TypeError: exceptions must derive from BaseException

可以看到没有实现基类bar方法的Concrete类,依然可以使用,并没有在第一时间抛出错误。

要解决这个问题,就要用到我们abc模块了。

from abc import ABC
# 定义基类,继承于ABC
>>> class Base(ABC):
	@abstractmethod
	def foo(self):
		pass
	@abstractmethod
	def bar(self):
		pass

	
>>> class Concrete(Base):
	def foo(self):
		print("called foo")
	# 实现类并没有实现bar方法

>>> c = Concrete()
Traceback (most recent call last):
 File "<pyshell#357>", line 1, in <module>
  c = Concrete()
TypeError: Can't instantiate abstract class Concrete with abstract methods bar

可以看到,在使用Concrete构造方法的时候,就立即抛出TypeError了。

0x04 使用namedtuple的好处

关于namedtuple的用法在前面的文章《如何在Python中表示一个对象》 也有提到。

简单的说namedtuple是一个可以命名的tuple,他是对tuple的扩展,它有跟tuple一样不可变的属性。

对于一些数据类的定义,namedtuple使用起来非常方便

>>> from collections import namedtuple
>>> Point = namedtuple('Point','x y z')
>>> p = Point(1,3,5)
>>> p
Point(x=1, y=3, z=5)
>>> p.x
1
>>> p.y = 3.5
AttributeError: can't set attribute
# 可以看出通过namedtuple定义对象,就是一个class类型的
>>> type(p)
<class '__main__.Point'>

还可以使用它内部的一些工具方法,在实际的编码当中也是非常实用的。

# 转化为dict
>>> p._asdict()
OrderedDict([('x', 1), ('y', 3), ('z', 5)])
# 更新或替换某个属性值
>>> p._replace(x=111)
Point(x=111, y=3, z=5)
# 使用_make创建新对象
>>> Point._make([333,666,999])
Point(x=333, y=666, z=999)

0x05 类变量和实例变量

Python中对象的属性类型有实例变量和类变量。

类变量是属于类的,它存储在“类的内存空间”里,并能够被它的各个实例对象共享。而实例变量是属于某个特定实例的,它不在“类的内存空间”中,它是独立于各个实例存在的。

>>> class Cat:
	num_legs = 4
	
>>> class Cat:
	num_legs = 4
	def __init__(self,name):
		self.name = name

>>> tom = Cat('tom')
>>> jack = Cat('jack')
>>> tom.name,jack.name
('tom', 'jack')
>>> tom.num_legs,jack.num_legs
(4, 4)
>>> Cat.num_legs
4

这里定义了一个猫类,它有一个实例变量name,还有一个类变量num_legs,这个是各个实例共享的。

如果对类变量进行,那么其它实例也会同步修改。而对某个实例对修改,并不会影响都类变量。

>>> Cat.num_legs = 6
>>> tom.num_legs,jack.num_legs
(6, 6)
>>> tom.num_legs = 2
>>> tom.num_legs,jack.num_legs
(2, 6)
>>> Cat.num_legs
6

tom.num_legs = 2这个语句都作用其实对tom这个实例增加了一个属性,只不过这个属性名称跟类属性的名称是一致的。

0x06 实例方法、类方法和静态方法

为了更好区分,我们还是来看代码

>>> class MyClass:
	def method(self):
		print(f"instance method at {self}" )
	@classmethod
	def classmethod(cls):
		print(f'classmethod at {cls}')
	@staticmethod
	def staticmethod():
		print('staticmethod')

		
>>> mc = MyClass()
>>> mc.method
<bound method MyClass.method of <__main__.MyClass object at 0x10c280b00>>
>>> mc.classmethod
<bound method MyClass.classmethod of <class '__main__.MyClass'>>
>>> mc.staticmethod
<function MyClass.staticmethod at 0x1090d4378>

可以看到在MyClass中分别定义实例方法(method)、类方法(classmethod)和静态方法(staticmethod)

在Python中一切都是对象,所以我打印来一下各个方法的__repr__输出。

对于实例方法method是绑定在MyClass的具体实现对象中的,而类方法classmethod是绑定在MyClass中的,而静态方法staticmethod既不绑定在实例里,也不绑定在类中,它就是一个function对象实例方法的调用,需要传递一个实例对象到实例方法中,以下两种方法的调用是等价的。

>>> mc.method()
instance method at <__main__.MyClass object at 0x10910ada0>
>>> MyClass.method(mc)
instance method at <__main__.MyClass object at 0x10910ada0>

类方法的调用和静态方法的调用都是使用ClassName.methodName()的方式。

>>> MyClass.classmethod()
classmethod at <class '__main__.MyClass'>
>>> MyClass.staticmethod()
staticmethod

类方法和静态方法有什么区别呢?

  • 首先在前面可以看到类方法和静态方法的对象是不一样的,一个是bound method,一个是function。
  • 其次类方法可以访问到类对象MyClass,而静态方法不能。
  • 最后静态方法其实跟一个普通的function对象一样,只不过它是属于类命名空间的。

0x07 总结一下

本文主要对Python中一些常见的面向对象的相关的一些特性进行了说明。包括对象的比较、输出、拷贝等操作,以及推荐使用namedtuple定义数据类。最后对类变量和实例变量以及类方法、实例方法和静态方法的不同作了分析。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现的金山快盘的签到程序
Jan 17 Python
Python 实现文件的全备份和差异备份详解
Dec 27 Python
浅谈flask中的before_request与after_request
Jan 20 Python
Python实现的朴素贝叶斯算法经典示例【测试可用】
Jun 13 Python
flask入门之表单的实现
Jul 18 Python
Python随机生成身份证号码及校验功能
Dec 04 Python
使用Python横向合并excel文件的实例
Dec 11 Python
Python反爬虫技术之防止IP地址被封杀的讲解
Jan 09 Python
Django用户认证系统 User对象解析
Aug 02 Python
Python读取csv文件实例解析
Dec 30 Python
python与mysql数据库交互的实现
Jan 06 Python
python not运算符的实例用法
Jun 30 Python
python实现函数极小值
Jul 10 #Python
通过PHP与Python代码对比的语法差异详解
Jul 10 #Python
python 梯度法求解函数极值的实例
Jul 10 #Python
对Python中小整数对象池和大整数对象池的使用详解
Jul 09 #Python
Python 旋转打印各种矩形的方法
Jul 09 #Python
python opencv对图像进行旋转且不裁剪图片的实现方法
Jul 09 #Python
python下的opencv画矩形和文字注释的实现方法
Jul 09 #Python
You might like
收音机发烧友应当熟知的100条知识
2021/03/02 无线电
PHP安全性漫谈
2012/06/28 PHP
PHP的Yii框架中过滤器相关的使用总结
2016/03/29 PHP
php版微信支付api.mch.weixin.qq.com域名解析慢原因与解决方法
2016/10/12 PHP
PHP实现RTX发送消息提醒的实例代码
2017/01/03 PHP
Laravel find in set排序实例
2019/10/09 PHP
ext jquery 简单比较
2010/04/07 Javascript
js实现GridView单选效果自动设置交替行、选中行、鼠标移动行背景色
2010/05/27 Javascript
jquery 结合C#后台的数组对文章的关键字自动添加链接的代码
2011/07/15 Javascript
JQuery设置文本框和密码框得到焦点时的样式
2013/08/30 Javascript
JavaScript简单实现鼠标拖动选择功能
2014/03/06 Javascript
js+CSS实现弹出居中背景半透明div层的方法
2015/02/26 Javascript
Angularjs中如何使用filterFilter函数过滤
2016/02/06 Javascript
Jq通过td获取同行其它列td的方法
2016/10/05 Javascript
js实现上传图片预览方法
2016/10/25 Javascript
详解js的六大数据类型
2016/12/27 Javascript
vue拦截器Vue.http.interceptors.push使用详解
2017/04/22 Javascript
深入浅出webpack之externals的使用
2017/12/04 Javascript
jQuery阻止事件冒泡实例分析
2018/07/03 jQuery
JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析
2019/03/06 Javascript
微信小游戏之使用three.js 绘制一个旋转的三角形
2019/06/10 Javascript
Python(Tornado)模拟登录小米抢手机
2013/11/12 Python
Python 解决中文写入Excel时抛异常的问题
2018/05/03 Python
python3.7 使用pymssql往sqlserver插入数据的方法
2019/07/08 Python
python实现各种插值法(数值分析)
2019/07/30 Python
用openCV和Python 实现图片对比,并标识出不同点的方式
2019/12/19 Python
python代码实现TSNE降维数据可视化教程
2020/02/28 Python
全球性的奢侈品梦工厂:Forzieri(福喜利)
2019/02/20 全球购物
说说你所熟悉或听说过的j2ee中的几种常用模式?及对设计模式的一些看法
2012/05/24 面试题
Android面试宝典
2013/08/06 面试题
SQL语言面试题
2013/08/27 面试题
财务管理专业推荐信
2013/11/19 职场文书
大宝sod蜜广告词
2014/03/21 职场文书
师德师风整改措施
2014/10/24 职场文书
学生会部长竞选稿
2015/11/19 职场文书
详解Django中 render() 函数的使用方法
2021/04/22 Python