全面理解Python中self的用法


Posted in Python onJune 04, 2016

刚开始学习Python的类写法的时候觉得很是麻烦,为什么定义时需要而调用时又不需要,为什么不能内部简化从而减少我们敲击键盘的次数?你看完这篇文章后就会明白所有的疑问。

self代表类的实例,而非类。

实例来说明:

class Test:
  def prt(self):
    print(self)
    print(self.__class__)
 
t = Test()
t.prt()

执行结果如下

<__main__.Test object at 0x000000000284E080>
<class '__main__.Test'>

从上面的例子中可以很明显的看出,self代表的是类的实例。而self.class则指向类。

self不必非写成self

有很多童鞋是先学习别的语言然后学习Python的,所以总觉得self怪怪的,想写成this,可以吗?

当然可以,还是把上面的代码改写一下。

class Test:
  def prt(this):
    print(this)
    print(this.__class__)
 
t = Test()
t.prt()

改成this后,运行结果完全一样。

当然,最好还是尊重约定俗成的习惯,使用self。

self可以不写吗

在Python的解释器内部,当我们调用t.prt()时,实际上Python解释成Test.prt(t),也就是说把self替换成类的实例。

有兴趣的童鞋可以把上面的t.prt()一行改写一下,运行后的实际结果完全相同。

实际上已经部分说明了self在定义时不可以省略,如果非要试一下,那么请看下面:

class Test:
  def prt():
    print(self)
 
t = Test()
t.prt()

运行时提醒错误如下:prt在定义时没有参数,但是我们运行时强行传了一个参数。

由于上面解释过了t.prt()等同于Test.prt(t),所以程序提醒我们多传了一个参数t。

Traceback (most recent call last):
 File "h.py", line 6, in <module>
  t.prt()
TypeError: prt() takes 0 positional arguments but 1 was given

当然,如果我们的定义和调用时均不传类实例是可以的,这就是类方法。

class Test:
  def prt():
    print(__class__)
Test.prt()

运行结果如下

<class '__main__.Test'>

在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例。

先看代码

class Parent:
  def pprt(self):
    print(self)
 
class Child(Parent):
  def cprt(self):
    print(self)
c = Child()
c.cprt()
c.pprt()
p = Parent()
p.pprt()

运行结果如下

<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>

解释:

运行c.cprt()时应该没有理解问题,指的是Child类的实例。

但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。

在描述符类中,self指的是描述符类的实例

不太容易理解,先看实例:

class Desc:
  def __get__(self, ins, cls):
    print('self in Desc: %s ' % self )
    print(self, ins, cls)
class Test:
  x = Desc()
  def prt(self):
    print('self in Test: %s' % self)
t = Test()
t.prt()
t.x

运行结果如下:

self in Test: <__main__.Test object at 0x0000000002A570B8>
self in Desc: <__main__.Desc object at 0x000000000283E208>
<__main__.Desc object at 0x000000000283E208> <__main__.Test object at 0x0000000002A570B8> <class '__main__.Test'>

大部分童鞋开始有疑问了,为什么在Desc类中定义的self不是应该是调用它的实例t吗?怎么变成了Desc类的实例了呢?

注意:此处需要睁大眼睛看清楚了,这里调用的是t.x,也就是说是Test类的实例t的属性x,由于实例t中并没有定义属性x,所以找到了类属性x,而该属性是描述符属性,为Desc类的实例而已,所以此处并没有顶用Test的任何方法。

那么我们如果直接通过类来调用属性x也可以得到相同的结果。

下面是把t.x改为Test.x运行的结果。

self in Test: <__main__.Test object at 0x00000000022570B8>
self in Desc: <__main__.Desc object at 0x000000000223E208>
<__main__.Desc object at 0x000000000223E208> None <class '__main__.Test'>

题外话:由于在很多时候描述符类中仍然需要知道调用该描述符的实例是谁,所以在描述符类中存在第二个参数ins,用来表示调用它的类实例,所以t.x时可以看到第三行中的运行结果中第二项为<main.Test object at 0x0000000002A570B8>。而采用Test.x进行调用时,由于没有实例,所以返回None。

从OO的本质理解python中的self
举个栗子,假设我要对用户的数据进行操作,用户的数据包含name和age。如果用面向过程的话,实现出来是下面这样子的。

def user_init(user,name,age): 
  user['name'] = name 
  user['age'] = age 
 
def set_user_name(user, x): 
  user['name'] = x 
 
def set_user_age(user, x): 
  user['age'] = x 
 
def get_user_name(user): 
  return user['name'] 
 
def get_user_age(user): 
  return user['age'] 
 
myself = {} 
user_init(myself,'kzc',17) 
print get_user_age(myself) 
set_user_age(myself,20) 
print get_user_age(myself)

可以看到,对用户的各种操作,都要传user参数进去。
如果用面向对象的话,就不用每次把user参数传来传去,把相关的数据和操作绑定在一个地方,在这个类的各个地方,可以方便的获取数据。
之所以可以在类中的各个地方访问数据,本质就是绑定了self这个东西,它方法的第一个参数,当然可以不叫self,叫其它名字,self只不过是个约定。
下面是面向对象的实现,可以看到,结构化多了,清晰可读。

class User(object): 
  def __init__(self,name,age): 
    self.name = name 
    self.age = age 
 
  def SetName(self,name): 
    self.name = name 
 
  def SetAge(self,age): 
    self.age = age 
 
  def GetName(self): 
    return self.name 
 
  def GetAge(self): 
    return self.age 
 
u = User('kzc',17) 
print u.GetName() 
print u.GetAge()

从上面这个例子可以看出,其实面向对象挺有用的,只不过大多数人抽象的不好,封装的不好,错误的运用。

总结

  • self在定义时需要定义,但是在调用时会自动传入。
  • self的名字并不是规定死的,但是最好还是按照约定是用self
  • self总是指调用时的类的实例。
Python 相关文章推荐
零基础写python爬虫之打包生成exe文件
Nov 06 Python
探究数组排序提升Python程序的循环的运行效率的原因
Apr 01 Python
PyQt5每天必学之像素图控件QPixmap
Apr 19 Python
对python文件读写的缓冲行为详解
Feb 13 Python
Python Pandas数据中对时间的操作
Jul 30 Python
python切片(获取一个子列表(数组))详解
Aug 09 Python
浅析python中while循环和for循环
Nov 19 Python
python super用法及原理详解
Jan 20 Python
python实现ip地址的包含关系判断
Feb 07 Python
Python编程快速上手——正则表达式查找功能案例分析
Feb 28 Python
解决在keras中使用model.save()函数保存模型失败的问题
May 21 Python
Pycharm连接远程服务器并远程调试的全过程
Jun 24 Python
举例讲解Python中字典的合并值相加与异或对比
Jun 04 #Python
详解Python中open()函数指定文件打开方式的用法
Jun 04 #Python
Python中import导入上一级目录模块及循环import问题的解决
Jun 04 #Python
Python输出汉字字库及将文字转换为图片的方法
Jun 04 #Python
使用Python的Flask框架来搭建第一个Web应用程序
Jun 04 #Python
Python的Django中将文件上传至七牛云存储的代码分享
Jun 03 #Python
详解duck typing鸭子类型程序设计与Python的实现示例
Jun 03 #Python
You might like
php中使用临时表查询数据的一个例子
2013/02/03 PHP
PHP simplexml_load_string()函数实例讲解
2019/02/03 PHP
php+ajax实现文件切割上传功能示例
2020/03/03 PHP
JavaScript Event学习第四章 传统的事件注册模型
2010/02/07 Javascript
js控制frameSet示例
2013/09/10 Javascript
js禁止页面刷新禁止用F5键刷新禁止右键的示例代码
2013/09/23 Javascript
jquery 删除字符串最后一个字符的方法解析
2014/02/11 Javascript
js实现大转盘抽奖游戏实例
2015/06/24 Javascript
JS实现超精简的链接列表在固定区域内滚动效果代码
2015/11/04 Javascript
详解从零搭建 vue2 vue-router2 webpack3 工程
2017/11/22 Javascript
Vue+webpack项目基础配置教程
2018/02/12 Javascript
angular2 组件之间通过service互相传递的实例
2018/09/30 Javascript
Vue仿微信app页面跳转动画效果
2019/08/21 Javascript
vue-父子组件和ref实例详解
2019/11/10 Javascript
详解python开发环境搭建
2016/12/16 Python
Python实现的井字棋(Tic Tac Toe)游戏示例
2018/01/31 Python
python使用tornado实现登录和登出
2018/07/28 Python
python tkinter界面居中显示的方法
2018/10/11 Python
python实现简单多人聊天室
2018/12/11 Python
使用python批量化音乐文件格式转换的实例
2019/01/09 Python
pycharm中显示CSS提示的知识点总结
2019/07/29 Python
python cv2在验证码识别中应用实例解析
2019/12/25 Python
Python列表解析操作实例总结
2020/02/26 Python
python3读取autocad图形文件.py实例
2020/06/05 Python
AmazeUI 按钮交互的实现示例
2020/08/24 HTML / CSS
Omio意大利:全欧洲低价大巴、火车和航班搜索和比价
2017/12/02 全球购物
Chain Reaction Cycles芬兰:世界上最大的在线自行车商店
2017/12/06 全球购物
Clarks鞋法国官方网站:英国其乐鞋品牌
2018/02/11 全球购物
美国宠物用品网站:Value Pet Supplies
2018/03/17 全球购物
北卡罗来纳州豪华家具和家居装饰店:Carolina Rustica
2018/10/30 全球购物
美国宠物美容和宠物用品购物网站:Cherrybrook
2018/12/07 全球购物
英文自荐信格式
2013/11/28 职场文书
技校毕业生个人学习的自我评价
2014/02/21 职场文书
中学生运动会新闻稿
2014/09/24 职场文书
pycharm2021激活码使用教程(永久激活亲测可用)
2021/03/30 Python
详解Anyscript开发指南绕过typescript类型检查
2022/09/23 Javascript