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

  1. class Test:
  2. def prt(self):
  3. print(self)
  4. print(self.__class__)
  5.  
  6. t = Test()
  7. t.prt()

执行结果如下

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

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

self不必非写成self

把上面的代码改写一下。

  1. class Test:
  2. def prt(this):
  3. print(this)
  4. print(this.__class__)
  5.  
  6. t = Test()
  7. t.prt()

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

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

self可以不写吗

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

把上面的t.prt()一行改写一下,运行后的实际结果完全相同。

实际上已经部分说明了self在定义时不可以省略

  1. class Test:
  2. def prt():
  3. print(self)
  4.  
  5. t = Test()
  6. t.prt()

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

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

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

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

  1. class Test:
  2. def prt():
  3. print(__class__)
  4. Test.prt()

运行结果如下

  1. <class '__main__.Test'>

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

先看代码

  1. class Parent:
  2. def pprt(self):
  3. print(self)
  4.  
  5. class Child(Parent):
  6. def cprt(self):
  7. print(self)
  8. c = Child()
  9. c.cprt()
  10. c.pprt()
  11. p = Parent()
  12. p.pprt()

运行结果如下

  1. <__main__.Child object at 0x0000000002A47080>
  2. <__main__.Child object at 0x0000000002A47080>
  3. <__main__.Parent object at 0x0000000002A47240>

解释:

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

但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,

所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。

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

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

  1. class Desc:
  2. def __get__(self, ins, cls):
  3. print('self in Desc: %s ' % self )
  4. print(self, ins, cls)
  5. class Test:
  6. x = Desc()
  7. def prt(self):
  8. print('self in Test: %s' % self)
  9. t = Test()
  10. t.prt()
  11. t.x

运行结果如下:

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

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

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

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

  1. self in Test: <__main__.Test object at 0x00000000022570B8>
  2. self in Desc: <__main__.Desc object at 0x000000000223E208>
  3. <__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。如果用面向过程的话,实现出来是下面这样子的。

  1. def user_init(user,name,age):
  2. user['name'] = name
  3. user['age'] = age
  4.  
  5. def set_user_name(user, x):
  6. user['name'] = x
  7.  
  8. def set_user_age(user, x):
  9. user['age'] = x
  10.  
  11. def get_user_name(user):
  12. return user['name']
  13.  
  14. def get_user_age(user):
  15. return user['age']
  16.  
  17. myself = {}
  18. user_init(myself,'kzc',17)
  19. print get_user_age(myself)
  20. set_user_age(myself,20)
  21. print get_user_age(myself)

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

  1. class User(object):
  2. def __init__(self,name,age):
  3. self.name = name
  4. self.age = age
  5.  
  6. def SetName(self,name):
  7. self.name = name
  8.  
  9. def SetAge(self,age):
  10. self.age = age
  11.  
  12. def GetName(self):
  13. return self.name
  14.  
  15. def GetAge(self):
  16. return self.age
  17.  
  18. u = User('kzc',17)
  19. print u.GetName()
  20. print u.GetAge()

总结

  • self在定义时需要定义,但是在调用时会自动传入。
  • self的名字并不是规定死的,但是最好还是按照约定是用self
  • self总是指调用时的类的实例。

全面理解python中self的用法的更多相关文章

  1. python 中del 的用法

    python中的del用法比较特殊,新手学习往往产生误解,弄清del的用法,可以帮助深入理解python的内存方面的问题. python的del不同于C的free和C++的delete. 由于pyth ...

  2. 理解 Python 中的可变参数 *args 和 **kwargs:

    默认参数:  Python是支持可变参数的,最简单的方法莫过于使用默认参数,例如: def getSum(x,y=5): print "x:", x print "y:& ...

  3. [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式

    使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...

  4. 【转】你真的理解Python中MRO算法吗?

    你真的理解Python中MRO算法吗? MRO(Method Resolution Order):方法解析顺序. Python语言包含了很多优秀的特性,其中多重继承就是其中之一,但是多重继承会引发很多 ...

  5. python中argparse模块用法实例详解

    python中argparse模块用法实例详解 这篇文章主要介绍了python中argparse模块用法,以实例形式较为详细的分析了argparse模块解析命令行参数的使用技巧,需要的朋友可以参考下 ...

  6. 深入理解Python中的yield和send

    send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互. 但是需要注意,在一个生成器对象没有执行next方法之前,由 ...

  7. 【313】python 中 print 函数用法总结

    参考:python 中 print 函数用法总结 参考:Python print() 函数(菜鸟教程) 参考:Python 3 print 函数用法总结 目录: 字符串和数值类型 变量 格式化输出 p ...

  8. 如何理解python中的if __name__=='main'的作用

    一. 一个浅显易懂的比喻 我们在学习python编程时,不可避免的会遇到if __name__=='main'这样的语句,它到底有什么作用呢? <如何简单地理解Python中的if __name ...

  9. python中MySQLdb模块用法实例

    篇文章主要介绍了python中MySQLdb模块用法,以实例形式详细讲述了MySQLdb模块针对MySQL数据库的各种常见操作方法,非常具有实用价值,需要的朋友可以参考下 本文实例讲述了python中 ...

随机推荐

  1. NX二次开发-UFUN获取面的内外边界UF_MODL_ask_loop_list_item

    NX11+VS2013 #include <uf.h> #include <uf_modl.h> #include <NXOpen/Face.hxx> #inclu ...

  2. hdu多校第十场 1003 (hdu6693) Valentine's Day 贪心/概率

    题意: 有许多物品,每个物品有一定概率让女朋友开心.你想让女朋友开心且只开心一次,让你挑一些物品,使得这个只开心一次的概率最大,求最大概率. 题解: 设物品i让女朋友开心的概率为$p_i$ 若你挑选了 ...

  3. 笔试题-求小于等于N的数中有多少组素勾股数

    题目描述: 一组勾股数满足:a2+b2=c2: 素勾股数:a,b,c彼此互质. 输入正整数N: 输出小于等于N的数中有多少组勾股数. 例: 输入:10 输出:1 思路:我是直接暴力破解的…… 首先找出 ...

  4. LeetCode 176. Second Highest Salary (第二高的薪水)

    题目标签: 题目给了我们一个工资表,让我们返回第二高的工资. 利用Max,把第一高的工资找到,然后利用 NOT IN,去找到第二高的工资. Java Solution: Runtime:  153ms ...

  5. 极限学习机(Extreme Learning Machine)学习笔记

    最近研究上了这个一个东西--极限学习机. 在很多问题中,我大多会碰到两个问题,一个是分类,另一个就是回归.简单来说,分类是给一串数打个标签,回归是把一串数变为一个数. 在这里我们需要处理的数据一般维度 ...

  6. 中文linux安装oracle界面乱码解决方案

    来自:http://blog.csdn.net/h249059945/article/details/12122853 在linux的中文操作系统下使用xmanager进行oracle进行安装的时候, ...

  7. flask-Local源码流程解析

    flask中Local源码数据类型首先明确:源码中要构造的数据类型数是这样的: __storage__ = { 用线程或者协程的唯一标识为键: {stack:[ctx(session/request) ...

  8. OpenLiveWriter博客工具

    1.OpenLiveWriter安装 官网下载地址:http://openlivewriter.org/ 默认安装到:C:\Users\用户\AppData\Local\OpenLiveWriter目 ...

  9. js获取table checkbox选中行的值.mdjs获取table checkbox选中行的

    <!DOCTYPE html> <html> <head> <script src="https://cdn.staticfile.org/jque ...

  10. CentOS 7 下配置 Nginx + PHP7.1 + MariaDB 以及 Laravel 框架

    <!doctype html> CentOS 7 下配置 Nginx + PHP7.1 + MariaDB 以及 Laravel 框架.mdhtml {overflow-x: initia ...