python类中的self与cls

python中的类是一种抽象的数据类型,是对一类具有相同属性和方法的对象的抽象,它定义了该集合中每个对象所共有的属性和方法,对象是类的实例

python中类就是一个抽象的集合,包含属性和方法。对象是类的实例,也就是类的具体实现。一般来说,在使用类中某个方法时,需要先实例化,在了解这两个概念之后,我们讲一下类中的selfcls

1. self与cls

在类中,selfcls都是指向类的实例的指针,但是self指向的是类的实例,而cls指向的是类本身。selfcls都可以用于访问类中的属性和方法

selfcls一般和@staticmethod、@classmethod一起使用,下面看一个基本的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A(object):
def foo1(self):
print("hello, ", self)

@staticmethod
def foo2():
print("hello")

@classmethod
def foo3(cls):
print("hello, ", cls)

a = A() # 初始化类实例
a.foo1() # hello, <__main__.A object at 0x7f396b06a6e0>
print(a) # <__main__.A object at 0x7f396b06a6e0>

a.foo2() # hello

a.foo3() # hello, <class '__main__.A'>
print(A) # <class '__main__.A'>

在上面例子中,我们定义了一个类A并初始化了一个类实例a。在类中,定义了三个函数foo1foo2foo3,其中foo2用装饰器@staticmethod装饰,代表这个函数是一个静态方法,foo3用装饰器@classmethod装饰,代表这个函数是一个类方法

从输出中可以看出,类实例a调用了foo1函数,输出hello与self,这里的self代表的就是实例化的a,可以看到self与print(a)的结果是相同的。而foo2foo3函数中,没有使用self,而是使用了cls,这里的cls代表的是类本身,可以看到cls与print(A)的结果是相同的

2. @statistic与@classmethod

有了上面基本的认识之后,我们更详细的讨论@staticmethod和@classmethod,self指向的是类的实例,而cls指向的是类本身,selfcls都可以访问类中的属性和方法

我们在看下面这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Student(object):
name = "harry" # 类属性
def __init__(self, name):
self.name = name # 实例属性

def say1(self): # 实例方法
print("hello, ", self)

@staticmethod
def say2(): # 静态方法
print("hello")

@classmethod
def say3(cls): # 类方法
print("hello, ", cls)

s = Student('Bob') # 实例化类
print(s.name) # Bob
print(Student.name) # harry

s.say1() # hello, <__main__.Student object at 0x7f9d7f7efa30>
Student.say1() # wrong
Student.say1(s) # true

s.say2() # hello
Student.say2() # hello

s.say3() # hello, <class '__main__.Student'>
Student.say3() # hello, <class '__main__.Student'>

我们构建了一个类Student,分别定义了类属性、实例属性、实例方法、静态方法、类方法。顾名思义,类属性类方法指的是类中固有属性和方法,实例属性和实例方法指的是类实例化之后的属性和方法。在上述例子中,我们从类Student中实例化了一个类实例s

  • 类属性和实例属性:类属性是类的固有属性,实例属性是类实例化之后的属性。在上面例子中,我们通过初始化实例s来覆盖掉了类属性name
  • 实例方法:实例方法是类实例化之后的方法,可以通过实例化类来调用,也可以通过类来调用,但是通过类来调用时,需要传入实例化的类。在上面例子中,我们通过实例化类s来调用了实例方法say1,而通过类Student来调用实例方法say1时,会报错,因为没有传入实例化的类,如果要不报错,需要传入实例化的类,即Student.say1(s)
  • 静态方法:静态方法是类的固有方法,可以通过实例化类来调用,也可以通过类来调用。在上面例子中,我们通过实例化类s来调用了静态方法say2,而通过类Student来调用静态方法say2时,也可以正常调用
  • 类方法:类方法是类的固有方法,可以通过实例化类来调用,也可以通过类来调用。在上面例子中,我们通过实例化类s来调用了类方法say3,而通过类Student来调用类方法say3时,也可以正常调用

3. 为什么要有静态方法和类方法

我们从一个例子来看类内各种方法的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A(object):
def foo1(self):
print("hello, ", self)

@staticmethod
def foo2():
print("hello")

@classmethod
def foo3(cls):
print("hello, ", cls)

a = A() # 初始化类实例
print(a.foo1) # <bound method A.foo1 of <__main__.A object at 0x7f50126b26e0>>
print(a.foo2) # <function A.foo2 at 0x7f5012505d80>
print(a.foo3) # <bound method A.foo3 of <class '__main__.A'>>

从输出结果来看一目了然

  • foo1是实例方法,他是面向对象的实例化对象的一个实例方法,即实例a的一个实例方法
  • foo2是静态方法,他是一个<function>,即他与类或者类实例都本质的关系,可以认为是在类外定义的一个函数,那为什么不在类外定义呢?我的理解是与语法糖有关,python是面向对象的编程,将函数写在类内起到了规范的作用
  • foo3是类方法,他是类A自带的方法,不需要实例化即可使用

为什么在类内要设置各种方法?

  • 为什么要用实例方法:需要改变实例属性的时候用到实例方法
  • 为什么要用类方法:需要改变类属性的时候用到类方法
  • 为什么要用静态方法:既不需要改变实例属性,也不需要改变类属性的时候用到静态方法
Error: API rate limit exceeded for 52.70.121.181. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)