最近在看代码,由于之前没有系统学习过Python,就有些知识点不是很清楚,这里整理一下,方便以后查阅。
Python中的@staticmethod\super.init和self
Python 装饰器
装饰器(decorators)是 Python 中的一种高级功能,它允许你动态地修改函数或类的行为。
装饰器是一种函数,它接受一个函数作为参数,并返回一个新的函数或修改原来的函数。
装饰器的语法使用 @decorator_name
来应用在函数或方法上。
Python 还提供了一些内置的装饰器,比如 @staticmethod
和 @classmethod
,用于定义静态方法和类方法。
装饰器的应用场景:
- 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
- 性能分析: 可以使用装饰器来测量函数的执行时间。
- 权限控制: 装饰器可用于限制对某些函数的访问权限。
- 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。
使用装饰器
装饰器通过 @
符号应用在函数定义之前,例如:
@time_logger
def target_function():
pass
等同于:
def target_function():
pass
target_function = time_logger(target_function)
这会将 target_function 函数传递给 decorator 装饰器,并将返回的函数重新赋值给 target_function。从而,每次调用 target_function 时,实际上是调用了经过装饰器处理后的函数。
通过装饰器,开发者可以在保持代码整洁的同时,灵活且高效地扩展程序的功能。
@staticmethod和@classmethod的作用与区别
作用
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。但是使用@staticmethod或@classmethod,就可以不需要实例化。
所以@staticmethod或@classmethod作用:使用@staticmethod或@classmethod,就可以不需要实例化,直接“类名.方法名()”来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
区别
- @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
- @classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。
对于类方法,可以通过类来调用,就像Test.foo(),有点类似C++中的静态方法, 也可以通过类的一个实例来调用,就像Test().foo(),这里Test(),写成这样之后它就是类的一个实例了。 - 静态方法它基本上跟一个全局函数相同,一般来说用的很少。
- 在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而@classmethod因为持有cls参数(当然,也可以用“self” 代替,个人认为是为了和类的self区分才用cls的),可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
代码演示
class A(object):
bar = 1
def foo(self):
print('foo')
@staticmethod
def static_foo():
print('static_foo')
print(A.bar)
@classmethod
def class_foo(cls):
print('class_foo')
print(cls.bar)
cls().foo()
A.static_foo()
A.class_foo()
'''输出结果
static_foo
1
class_foo
1
foo
'''
super() 函数
super()
函数是用于调用父类(超类)的一个方法。
super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
super().init()函数
super().__init__()
是用于在 Python 中调用父类(也称为超类或基类)的构造方法(init 方法)的一种方式。这通常在子类的构造方法中使用,以便在添加子类特有的功能之前,初始化父类中定义的属性和执行其他必要的设置。
super()
用来调用父类(基类)的方法,init()是类的构造方法,
super().__init__()
就是调用父类的__init__()方法, 同样可以使用super()去调用父类的其他方法。
super().init()基本用法
以下是一个基本示例,展示了如何在子类的构造方法中使用 super().init():
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # 调用父类的构造方法以初始化 name
self.age = age #添加自定义age属性
child = Child("Alice", 25)
print(child.name) # 输出 "Alice"
print(child.age) # 输出 25
super().init() 和 super(Child, self).init() 的区别
super().init() 和 super(Child, self).init() 都是用于调用父类的构造方法,但它们之间有一个重要的区别:
参数传递方式:
super().__init__()
: 这种方式不需要显式传递当前类和实例(self)作为参数,Python会自动识别当前类和实例。这是一种简化的写法,通常用于单继承的情况。super(Child, self).__init__()
: 这种方式明确指定了当前类(Child)和实例(self)作为参数传递给 super()。这种方式通常在多继承的情况下使用,以明确指定要调用哪个父类的构造方法。
多继承中的 super().init()
如果类是多继承的,使用 super(Child, self).init() 可以更明确地控制调用的是哪个父类的构造方法,因为 super() 可以基于参数中的当前类(Child)找到正确的父类。
下面是一个多继承的示例,演示了使用super().init() 和 super(Child, self).init() 的区别:
class Parent1:
def __init__(self):
print("Parent1 constructor")
class Parent2:
def __init__(self):
print("Parent2 constructor")
class Parent3:
def __init__(self):
print("Parent3 constructor")
class Child(Parent1,Parent2,Parent3): # 顺序为Parent1,Parent2,Parent3
def __init__(self):
super().__init__() # 使用简化写法,调用了 Parent1 的构造方法
child = Child()
#输出结果:Parent1 constructor
接下来将Child继承父类中Parent1,Parent2的顺序进行调换,运行结果如下:
#把Parent1,Parent2的顺序进行调换
class Parent1:
def __init__(self):
print("Parent1 constructor")
class Parent2:
def __init__(self):
print("Parent2 constructor")
class Parent3:
def __init__(self):
print("Parent3 constructor")
class Child(Parent2,Parent1,Parent3): # 顺序为Parent2,Parent1,Parent3
def __init__(self):
super().__init__() # 使用简化写法,调用了 Parent2 的构造方法
child = Child()
#输出结果:
#Parent2 constructor
在上面的示例中,我们可以知道,在多继承的情况下,super().init() 调用的是子类(Child)继承父类中,第一个父类的构造方法。
多继承中的 super(Child, self).init()
class Parent1:
def __init__(self):
print("Parent1 constructor")
class Parent2:
def __init__(self):
print("Parent2 constructor")
class Parent3:
def __init__(self):
print("Parent3 constructor")
class Child(Parent1, Parent2,Parent3):
def __init__(self):
super(Child, self).__init__() # 调用Child下一个父类的构造方法,即Parent1
super(Parent2, self).__init__() # 调用Parent2下一个父类的构造方法,即Parent3
child = Child()
#输出结果:
#Parent1 constructor
#Parent3 constructor
综上,我们可以知道,在多继承中,super().init() 继承的是子类继承的第一个父类,super(Parent2, self).init() 继承的是声明类的下一个父类
self 代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
以上实例执行结果为:
<__main__.Test instance at 0x100771878>
__main__.Test
从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:
class Test:
def prt(runoob):
print(runoob)
print(runoob.__class__)
t = Test()
t.prt()
以上实例执行结果为:
<__main__.Test instance at 0x100771878>
__main__.Test
在 Python中,self 是一个惯用的名称,用于表示类的实例(对象)自身。它是一个指向实例的引用,使得类的方法能够访问和操作实例的属性。
当你定义一个类,并在类中定义方法时,第一个参数通常被命名为 self,尽管你可以使用其他名称,但强烈建议使用 self,以保持代码的一致性和可读性。
class MyClass:
def __init__(self, value):
self.value = value
def display_value(self):
print(self.value)
# 创建一个类的实例
obj = MyClass(42)
# 调用实例的方法
obj.display_value() # 输出 42