用实例分析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判断操作系统类型代码分享
Nov 22 Python
老生常谈进程线程协程那些事儿
Jul 24 Python
使用Python写一个量化股票提醒系统
Aug 22 Python
python3实现点餐系统
Jan 24 Python
使用PyQt4 设置TextEdit背景的方法
Jun 14 Python
Python 仅获取响应头, 不获取实体的实例
Aug 21 Python
Python TCPServer 多线程多客户端通信的实现
Dec 31 Python
tensorflow之获取tensor的shape作为max_pool的ksize实例
Jan 04 Python
Selenium基于PIL实现拼接滚动截图
Apr 10 Python
pycharm sciview的图片另存为操作
Jun 01 Python
python 使用paramiko模块进行封装,远程操作linux主机的示例代码
Dec 03 Python
Python中生成ndarray实例讲解
Feb 22 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
MySQL数据库转移,access,sql server 转 MySQL 的图文教程
2007/09/02 PHP
destoon实现调用热门关键字的方法
2014/07/15 PHP
PHP实现加密的几种方式介绍
2015/02/22 PHP
PHP框架Laravel学习心得体会
2015/10/28 PHP
Windows2003下php5.4安装配置教程(IIS)
2016/06/30 PHP
php中的explode()函数实例介绍
2019/01/18 PHP
ThinkPHP3.2.3框架实现执行原生SQL语句的方法示例
2019/04/03 PHP
ThinkPHP类似AOP思想的参数验证的实现方法
2019/12/18 PHP
HTML DOM的nodeType值介绍
2011/03/31 Javascript
JQuery里面的几种选择器 查找满足条件的元素$(&quot;#控件ID&quot;)
2011/08/23 Javascript
JavaScript 函数参数是传值(byVal)还是传址(byRef) 分享
2013/07/02 Javascript
浅析js封装和作用域
2013/07/09 Javascript
jquery插件jTimer(jquery定时器)使用方法
2013/12/23 Javascript
table对象中的insertRow与deleteRow使用示例
2014/01/26 Javascript
关闭页面window.location事件未执行的原因及解决方法
2014/09/01 Javascript
了不起的node.js读书笔记之node的学习总结
2014/12/22 Javascript
javascript属性访问表达式用法分析
2015/04/25 Javascript
JavaScript中的this,call,apply使用及区别详解
2016/01/29 Javascript
BootStrap select2 动态改变值的方法
2017/02/10 Javascript
详解在Angularjs中ui-sref和$state.go如何传递参数
2017/04/24 Javascript
JS常用的几种数组遍历方式以及性能分析对比实例详解
2018/04/11 Javascript
vue.js 实现输入框动态添加功能
2018/06/25 Javascript
JavaScript设计模式之原型模式分析【ES5与ES6】
2018/07/26 Javascript
vue项目打包后上传至GitHub并实现github-pages的预览
2019/05/06 Javascript
浅谈用Python实现一个大数据搜索引擎
2017/11/28 Python
python表格存取的方法
2018/03/07 Python
JavaScript中的模拟事件和自定义事件实例分析
2018/07/27 Python
python BlockingScheduler定时任务及其他方式的实现
2019/09/19 Python
使用python的turtle绘画滑稽脸实例
2019/11/21 Python
Django crontab定时任务模块操作方法解析
2020/09/10 Python
事业单位个人应聘自荐信
2013/09/21 职场文书
团日活动总结书格式
2014/05/08 职场文书
企业环保标语
2014/06/10 职场文书
民族团结演讲稿范文
2014/08/27 职场文书
2016读书月活动心得体会
2016/01/14 职场文书
vue实现input输入模糊查询的三种方式
2022/08/14 Vue.js