类方法与静态方法
区别:类方法的第一个参数(例如cls)会自动绑定到类本身;静态方法不会绑定。
在类中,由@classmethod修饰的方法就是类方法;使用@staticmethod修饰的方法就是静态方法。
代码示例
class Dog(object):
# @classmethod类方法
@classmethod
def run(cls):
print("类方法run: ", cls)
# @staticmethod静态方法
@staticmethod
def info(message):
print("静态方法info:", message)
# 调用类方法,Dog类会自动绑定到第一个参数
Dog.run()
# 调用静态方法,不会绑定第一个形参,必须手动绑定
Dog.info("LOVE")
# 创建Dog对象
b = Dog()
# 使用对象调用run()类方法,其实依然还是使用类调用的
# 因此第一个参数依然被自动绑定到Dog类
b.run()
# 使用对象调用info()静态方法,其实依然还是使用类调用的
# 因此程序必须为第一个参数执行绑定
b.info('message')
总结
- 使用@classmethod修饰的方法就是类方法,无论程序是使用类还是类的实例调用该方法,Python始终都会将类方法的第一个参数绑定到类本身。
- 使用@staticmethod修饰的方法就是静态方法,无论程序是使用类还是类的实例调用该方法,Python都不会将类方法的第一个参数绑定到类本身。
@函数装饰器
@classmethod和@staticmethod的本质就是函数装饰器,其中classmethod和staticmethod都是Python内置的函数。
当程序使用@函数(比如函数A)装饰另一个函数(比如函数B)时,实际完成如下两部:
(1)将被修饰的函数(函数B)作为参数传给@符号引用的函数(函数A)
(2)将函数B替换(装饰)成(1)的返回值
代码示例
def functionA(fn):
print("A")
fn() # 执行传入的fn参数
return “functionA"
"""
下面的装饰效果相当于functionA(functionB)
functionB将会被替换(装饰)成该语句的返回值
由于functionA函数返回”functionA“,因此functionB就是”functionA“
"""
@functionA
def functionB():
print("B")
print(functionB) # functionA
总结
上面的程序使用@functionA修饰functionB,这意味着程序要完成两步操作。
(1)将functionB作为functionA()的参数,也就是上面的粗体字代码相当于执行functionA(functionB)
(2)将functionB替换为(1)执行的结果,functionA()执行完成后返回"functionA",因此functionB不再是函数,而是被替换成一个字符串。
运行上面程序,可以看到如下输出结果:
A
B
functionA
函数装饰器有什么用?
被修饰的函数总是被替换成@符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由@符号所引用的函数的返回值决定—如果@符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数。
代码示例:
def foo(fn):
# 定义嵌套函数
def bar(*args):
print("===1===", args)
n = args[0]
print("===2===", n*(n-1))
# 查看传给foo函数的fn函数
print(fn.__name__)
fn(n*(n-1))
print("*"*15)
return fn(n*(n-1))
return bar
"""
下面的装饰效果相当于foo(my_test)
my_test将会被替换(装饰)为该语句的返回值
由于foo()函数返回bar函数,因此my_test就是bar
"""
@foo
def my_test(a):
print("==my_test函数==", a)
# 打印my_test函数,将看到实际上是bar函数
print(my_test) # <function foo.<locals> .bar at 0x000000000021FABFA8>
# 下面代码看上去是调用my_test(),其实是调用bar()函数
my_test(10)
my_test(6, 5)
"""
上面程序定义了一个装饰器函数foo,该函数执行完成后并不是返回普通值,而是返回bar函数(这是关键),这意味着被该@foo修饰的函数最终都会被替换成bar函数。
上面程序使用@foo修饰my_test()函数,因此程序同样会执行foo(my_test),并将my_test替换成foo()函数的返回值,这说明my_test已经被替换成bar函数。接下来程序两次调用my_test()函数,实际上就是调用bar()函数。
运行上面程,可以看到如下结果:
<function foo.<locals> .bar at 0x000000000021FABFA8>
===1=== (10,)
===2=== 90
my_test
==my_test函数== 90
***********************
==my_test 函数==90
===1=== (6, 5)
===2=== 30
my_test
===my_test函数=== 30
**************
==my_test函数== 30
"""