用实例分析Python中method的参数传递过程
什么是method?
function就是可以通过名字可以调用的一段代码,我们可以传参数进去,得到返回值。所有的参数都是明确的传递过去的。
method是function与对象的结合。我们调用一个方法的时候,有些参数是隐含的传递过去的。下文会详细介绍。
instancemethod
In[5]:classHuman(object): ...:def__init__(self,weight): ...:self.weight=weight ...:defget_weight(self): ...:returnself.weight ...: In[6]:Human.get_weight Out[6]:<unboundmethodHuman.get_weight>
这告诉我们get_weight是一个没有被绑定方法,什么叫做未绑定呢?继续看下去。
In[7]:Human.get_weight() --------------------------------------------------------------------------- TypeErrorTraceback(mostrecentcalllast) /home/yao/learn/insight_python/<ipython-input-7-a2b2c5cd2f8d>in<module>() ---->1Human.get_weight() TypeError:unboundmethodget_weight()mustbecalledwithHumaninstanceasfirstargument(gotnothinginstead)
未绑定的方法必须使用一个Human实例作为第一个参数来调用啊。那我们来试试
In[10]:Human.get_weight(Human(45)) Out[10]:45
果然成功了,但是一般情况下我们习惯这么使用。
In[11]:person=Human(45) In[12]:person.get_weight() Out[12]:45
这两种方式的结果一模一样。我们看下官方文档是怎么解释这种现象的。
Whenaninstanceattributeisreferencedthatisn'tadataattribute,itsclassissearched.
Ifthenamedenotesavalidclassattributethatisafunctionobject,amethodobjectis
createdbypacking(pointersto)theinstanceobjectandthefunctionobjectjustfoundtogether
inanabstractobject:thisisthemethodobject.Whenthemethodobjectiscalledwithan
argumentlist,anewargumentlistisconstructedfromtheinstanceobjectandtheargumentlist,
andthefunctionobjectiscalledwiththisnewargumentlist.
原来我们常用的调用方法(person.get_weight())是把调用的实例隐藏的作为一个参数self传递过去了,self只是一个普通的参数名称,不是关键字。
In[13]:person.get_weight Out[13]:<boundmethodHuman.get_weightof<__main__.Humanobjectat0x8e13bec>> In[14]:person Out[14]:<__main__.Humanat0x8e13bec>
我们看到get_weight被绑定在了person这个实例对象上。
总结下
- instancemethod就是实例对象与函数的结合。
- 使用类调用,第一个参数明确的传递过去一个实例。
- 使用实例调用,调用的实例被作为第一个参数被隐含的传递过去。
classmethod
In[1]:classHuman(object): ...:weight=12 ...:@classmethod ...:defget_weight(cls): ...:returncls.weight In[2]:Human.get_weight Out[2]:<boundmethodtype.get_weightof<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]:classHuman(object): ...:weight=12 ...:@classmethod ...:defget_weight(cls): ...:printcls In[2]:Human.get_weight() <class'__main__.Human'> In[3]:Human().get_weight() <class'__main__.Human'>
我们看到传递过去的都是Human类,不是Human的实例,两种方式调用的结果没有任何区别。cls只是一个普通的函数参数,调用时被隐含的传递过去。
总结起来
- classmethod是类对象与函数的结合。
- 可以使用类和类的实例调用,但是都是将类作为隐含参数传递过去。
- 使用类来调用classmethod可以避免将类实例化的开销。
staticmethod
In[1]:classHuman(object): ...:@staticmethod ...:defadd(a,b): ...:returna+b ...:defget_weight(self): ...:returnself.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().addisHuman().add Out[6]:True In[7]:Human().get_weightisHuman().get_weight Out[7]:False
add在两个实例上也是同一个对象。instancemethod就不一样了,每次都会创建一个新的get_weight对象。
总结下
- 当一个函数逻辑上属于一个类又不依赖与类的属性的时候,可以使用staticmethod。
- 使用staticmethod可以避免每次使用的时都会创建一个对象的开销。
- staticmethod可以使用类和类的实例调用。但是不依赖于类和类的实例的状态。