用实例分析Python中method的参数传递过程


Posted in Python onApril 02, 2015

什么是method?

function就是可以通过名字可以调用的一段代码,我们可以传参数进去,得到返回值。所有的参数都是明确的传递过去的。
method是function与对象的结合。我们调用一个方法的时候,有些参数是隐含的传递过去的。下文会详细介绍。
instancemethod
 

In [5]: class Human(object):
  ...:   def __init__(self, weight):
  ...:     self.weight = weight
  ...:   def get_weight(self):
  ...:     return self.weight
  ...:  
 
In [6]: Human.get_weight
Out[6]: <unbound method Human.get_weight>

这告诉我们get_weight是一个没有被绑定方法,什么叫做未绑定呢?继续看下去。
 

In [7]: Human.get_weight()
---------------------------------------------------------------------------
TypeError                 Traceback (most recent call last)
/home/yao/learn/insight_python/<ipython-input-7-a2b2c5cd2f8d> in <module>()
----> 1 Human.get_weight()
 
TypeError: unbound method get_weight() must be called with Human instance as first argument (got nothing instead)

未绑定的方法必须使用一个Human实例作为第一个参数来调用啊。那我们来试试
 

In [10]: Human.get_weight(Human(45))
Out[10]: 45

果然成功了,但是一般情况下我们习惯这么使用。
 

In [11]: person = Human(45)
 
In [12]: person.get_weight()
Out[12]: 45

这两种方式的结果一模一样。我们看下官方文档是怎么解释这种现象的。
 
When an instance attribute is referenced that isn't a data attribute, its class is searched.
If the name denotes a valid class attribute that is a function object, a method object is
created by packing (pointers to) the instance object and the function object just found together
in an abstract object: this is the method object. When the method object is called with an
argument list, a new argument list is constructed from the instance object and the argument list,
and the function object is called with this new argument list.

原来我们常用的调用方法(person.get_weight())是把调用的实例隐藏的作为一个参数self传递过去了, self 只是一个普通的参数名称,不是关键字。
 

In [13]: person.get_weight
Out[13]: <bound method Human.get_weight of <__main__.Human object at 0x8e13bec>>
 
In [14]: person
Out[14]: <__main__.Human at 0x8e13bec>

我们看到get_weight被绑定在了 person 这个实例对象上。
总结下

  1.     instance method 就是实例对象与函数的结合。
  2.     使用类调用,第一个参数明确的传递过去一个实例。
  3.     使用实例调用,调用的实例被作为第一个参数被隐含的传递过去。

classmethod
 

In [1]: class Human(object):
  ...:   weight = 12
  ...:   @classmethod
  ...:   def get_weight(cls):
  ...:     return cls.weight
 
In [2]: Human.get_weight
Out[2]: <bound method type.get_weight of <class '__main__.Human'>>

我们看到get_weight是一个绑定在 Human 这个类上的method。调用下看看
 

In [3]: Human.get_weight()
Out[3]: 12
In [4]: Human().get_weight()
Out[4]: 12

类和类的实例都能调用 get_weight 而且调用结果完全一样。
我们看到 weight 是属于 Human 类的属性,当然也是 Human 的实例的属性。那传递过去的参数 cls 是类还是实例呢?
 

In [1]: class Human(object):
  ...:   weight = 12
  ...:   @classmethod
  ...:   def get_weight(cls):
  ...:     print cls
 
In [2]: Human.get_weight()
<class '__main__.Human'>
 
In [3]: Human().get_weight()
<class '__main__.Human'>

我们看到传递过去的都是 Human 类,不是 Human 的实例,两种方式调用的结果没有任何区别。cls 只是一个普通的函数参数,调用时被隐含的传递过去。
总结起来

  1.     classmethod 是类对象与函数的结合。
  2.     可以使用类和类的实例调用,但是都是将类作为隐含参数传递过去。
  3.     使用类来调用 classmethod 可以避免将类实例化的开销。

staticmethod
 

In [1]: class Human(object):
  ...:   @staticmethod
  ...:   def add(a, b):
  ...:     return a + b
  ...:   def get_weight(self):
  ...:     return self.add(1, 2)
 
In [2]: Human.add
Out[2]: <function __main__.add>
 
In [3]: Human().add
Out[3]: <function __main__.add>
 
In [4]: Human.add(1, 2)
Out[4]: 3
 
In [5]: Human().add(1, 2)
Out[5]: 3

我们看到 add 在无论是类还是实例上都只是一个普通的函数,并没有绑定在任何一个特定的类或者实例上。可以使用类或者类的实例调用,并且没有任何隐含参数的传入。
 

In [6]: Human().add is Human().add
Out[6]: True
 
In [7]: Human().get_weight is Human().get_weight
Out[7]: False

add 在两个实例上也是同一个对象。instancemethod 就不一样了,每次都会创建一个新的 get_weight 对象。
总结下

  1.     当一个函数逻辑上属于一个类又不依赖与类的属性的时候,可以使用 staticmethod。
  2.     使用 staticmethod 可以避免每次使用的时都会创建一个对象的开销。
  3.     staticmethod 可以使用类和类的实例调用。但是不依赖于类和类的实例的状态。
Python 相关文章推荐
python中字符串前面加r的作用
Jun 04 Python
一篇文章快速了解Python的GIL
Jan 12 Python
Python callable()函数用法实例分析
Mar 17 Python
浅谈Python中的作用域规则和闭包
Mar 20 Python
利用Python实现原创工具的Logo与Help
Dec 03 Python
Python multiprocess pool模块报错pickling error问题解决方法分析
Mar 20 Python
Django框架orM与自定义SQL语句混合事务控制操作
Jun 27 Python
tensorflow自定义激活函数实例
Feb 04 Python
python 已知一个字符,在一个list中找出近似值或相似值实现模糊匹配
Feb 29 Python
Python实现一个简单的递归下降分析器
Aug 01 Python
关于Python不换行输出和不换行输出end=““不显示的问题(亲测已解决)
Oct 27 Python
Python 删除List元素的三种方法remove、pop、del
Nov 16 Python
使用优化器来提升Python程序的执行效率的教程
Apr 02 #Python
使用Python脚本对Linux服务器进行监控的教程
Apr 02 #Python
在Python编程过程中用单元测试法调试代码的介绍
Apr 02 #Python
用Python的Django框架完成视频处理任务的教程
Apr 02 #Python
用map函数来完成Python并行任务的简单示例
Apr 02 #Python
对于Python异常处理慎用“except:pass”建议
Apr 02 #Python
Python的设计模式编程入门指南
Apr 02 #Python
You might like
自动分页的不完整解决方案
2007/01/12 PHP
php 缓存函数代码
2008/08/27 PHP
PHP中输出转义JavaScript代码的实现代码
2011/04/22 PHP
php实现水仙花数的4个示例分享
2014/04/08 PHP
php的mkdir()函数创建文件夹比较安全的权限设置方法
2014/07/28 PHP
php 流程控制switch的简单实例
2016/06/07 PHP
详解cookie验证的php应用的一种SSO解决办法
2017/10/20 PHP
微信公众号实现扫码获取微信用户信息(网页授权)
2019/04/09 PHP
html 锁定页面(js遮罩层弹出div效果)
2009/10/27 Javascript
JavaScript性能陷阱小结(附实例说明)
2010/12/28 Javascript
javascript的创建多行字符串的7种方法
2014/04/29 Javascript
nodejs实现遍历文件夹并统计文件大小
2015/05/28 NodeJs
基于JS判断iframe是否加载成功的方法(多种浏览器)
2016/05/13 Javascript
JS实现兼容火狐及IE iframe onload属性的遮罩层隐藏及显示效果
2016/08/23 Javascript
详解node nvm进行node多版本管理
2017/10/21 Javascript
浅谈vue-cli 3.0.x 初体验
2018/04/11 Javascript
微信小程序实现人脸识别
2018/05/25 Javascript
个人小程序接入支付解决方案
2019/05/23 Javascript
[44:01]2018DOTA2亚洲邀请赛3月30日 小组赛B组 EG VS paiN
2018/03/31 DOTA
python利用标准库如何获取本地IP示例详解
2017/11/01 Python
Python列表生成式与生成器操作示例
2018/08/01 Python
pytorch 中的重要模块化接口nn.Module的使用
2020/04/02 Python
python爬虫---requests库的用法详解
2020/09/28 Python
HTML5 window/iframe跨域传递消息 API介绍
2013/08/26 HTML / CSS
美国在线宠物用品商店:Entirely Pets
2017/01/01 全球购物
东南亚旅游平台:The Trip Guru
2018/01/01 全球购物
Probikekit欧盟:在线公路自行车专家
2019/07/12 全球购物
《鸟的天堂》教学反思
2014/02/27 职场文书
2014年机关党建工作总结
2014/11/11 职场文书
学校捐书活动总结
2015/05/08 职场文书
考研英语辞职信
2015/05/13 职场文书
学习经验交流会总结
2015/11/02 职场文书
大学学生会主席竞选稿
2015/11/19 职场文书
转变工作作风心得体会
2016/01/23 职场文书
Python Django项目和应用的创建详解
2021/11/27 Python
Spring this调用当前类方法无法拦截的示例代码
2022/03/20 Java/Android