方法调用与 self
Python 类中的方法与普通函数只有一个区别:第一个参数是实例本身的引用。这个参数通常命名为 self,由调用方隐式传入。理解 self 的工作机制,是理解 Python 面向对象设计的关键。
方法的本质
类中定义的函数,在通过类访问时是未绑定方法(unbound method),在通过实例访问时是绑定方法(bound method):
class Dog(object):
def bark(self):
return "woof!"
print Dog.bark # <unbound method Dog.bark>
d = Dog()
print d.bark # <bound method Dog.bark of <__main__.Dog object>>
绑定方法已经把实例 d "绑定"到方法的 self 参数上。调用 d.bark() 时,不需要传 self:
print d.bark() # woof!
# 等价于
print Dog.bark(d) # woof!,显式传入 self
self 不是关键字
self 只是约定俗成的参数名,不是关键字。你可以用其他名字,但绝对不要这样做:
class Bad(object):
def bark(this): # 语法合法,但极其不推荐
return "woof!"
用 self 是 Python 社区的铁律。IDE、文档生成器、其他程序员都依赖这个约定。
方法调用过程
当调用 d.bark() 时,Python 执行以下步骤:
- 在
d.__dict__中查找bark—— 找不到 - 在
Dog.__dict__中查找bark—— 找到函数对象 - 因为是通过实例访问,把
d和函数对象打包成绑定方法 - 调用绑定方法,自动把
d作为第一个参数传入
如果通过类访问(Dog.bark(d)),步骤 3 跳过,需要显式传入实例。
在方法中调用其他方法
通过 self 调用同一实例的其他方法:
class Calculator(object):
def __init__(self):
self.result = 0
def add(self, x):
self.result += x
return self
def multiply(self, x):
self.result *= x
return self
def get_result(self):
return self.result
def calculate(self):
self.add(5).multiply(2).add(3)
return self.get_result()
calc = Calculator()
print calc.calculate() # 13,(0 + 5) * 2 + 3
每个方法返回 self,支持方法链式调用(method chaining)。
方法外的函数
函数定义不必在类体内部。可以把外部函数赋值给类属性:
def external_bark(self):
return "%s says woof!" % self.name
class Dog(object):
bark = external_bark
def __init__(self, name):
self.name = name
d = Dog("Fido")
print d.bark() # Fido says woof!
这种技巧在动态修改类行为时有用,但通常会让代码更难理解,谨慎使用。
方法对象的内省
绑定方法对象有额外属性:
d = Dog("Fido")
method = d.bark
print method.im_self # <__main__.Dog object>,绑定的实例
print method.im_func # <function external_bark>,底层函数
print method.im_class # <class '__main__.Dog'>,定义方法的类
这些属性在元编程、调试、框架开发中很有用。
全局作用域中的方法
方法内部可以访问全局名称(模块级别的变量和函数),但类本身不会成为方法的全局作用域:
module_var = 42
class MyClass(object):
class_var = 100
def method(self):
print module_var # 42,可以访问模块全局变量
print self.class_var # 100,通过 self 访问类变量
# print class_var # NameError!类不是全局作用域
方法的全局作用域是定义它的模块,不是类。访问类变量必须通过 self 或类名。