方法
定义在类中的函数称为方法。根据调用的方式不同,分为:
- 对象方法
- 类方法
- 静态方法
- 魔术方法。
对象方法
定义在类中的普通函数,一般通过对象调用称为对象方法。
对象方法的定义
为了讲清楚对象方法的定义和调用,先看下面的案例
案例:
定义函数my_print
,它接收一个Point
对象,然后打印这个点的x,y坐标。
def my_print(point):
print('({},{})'.format(point.x, point.y))
p = Point()
p.x = 1
p.y = 2
my_print(p)
输出:
(1,2)
在定义函数distance
,它接收两个Point
对象,然后返回这两个点的距离。
def distance(p1, p2):
return ((p1.x-p2.x)**2 + (p1.y-p2.y)**2)**0.5
p1 = Point() # 实例化
p2 = Point() # 实例化
p1.x = 1 # 赋值x坐标
p1.y = 2 # 赋值y坐标
p2.x = 3 # 赋值x坐标
p2.y = 4 # 赋值y坐标
res = distance(p1,p2)
print(res)
输出:
2.8284271247461903
观察上面的两个函数,发现它们都接收一个或多个Point的对象作为参数。为了显式的加强这样的联系,我们可以将它们定义在Point的类中。
class Point:
"""
表示平面坐标系中的一个点
"""
name = '点'
def my_print(point):
print('({},{})'.format(point.x, point.y))
def distance(p1, p2):
return ((p1.x-p2.x)**2 + (p1.y-p2.y)**2)**0.5
对象方法的调用
对象方法像属性一样,可以通过句点法进行调用。
类.方法名(参数)
对象.方法名(参数)
通过类调用时,和普通函数没有区别。
# 接上面的代码
# 更新了类,再次实例化对象
point = Point()
point.x = 1
point.y = 2
p1 = Point()
p2 = Point()
p1.x = 1
p1.y = 2
p2.x = 3
p2.y = 4
通过类调用(一般不会这么调用)
Point.my_print(point)
res = Point.distance(p1, p2)
print(res)
输出:
(1,2)
2.8284271247461903
通过对象调用对象方法时,对象本身会被隐式的传给方法的第一个参数。
point.my_print()
res = p1.distance(p2)
print(res)
输出:
(1,2)
2.8284271247461903
因此,定义对象方法会习惯性的把第一个形参定义为self
,表示调用对象本身
class Point:
"""
表示平面坐标系中的一个点
"""
name = '点'
def my_print(self):
print('({},{})'.format(self.x, self.y))
def distance(self, p2):
return ((self.x-p2.x)**2 + (self.y-p2.y)**2)**0.5
类方法
在类中通过装饰器classmethod
来定义类方法。
调用类方法时,会把类自己作为第一个实参,就像一个实例方法把实例自己作为第一个实参。
案例:
定义一个类方法base_point
用来返回坐标原点。
class Point:
"""
表示平面坐标系中的一个点
"""
name = '点'
def my_print(self):
print('({},{})'.format(self.x, self.y))
def distance(self, p2):
return ((self.x-p2.x)**2 + (self.y-p2.y)**2)**0.5
@classmethod
def base_point(cls):
bp = cls()
bp.x = 0
bp.y = 0
return bp
通过类本身或者是该类的实例都可以调用类方法。
p = Point()
bp1 = p.base_point()
bp1.my_print()
bp2 = Point.base_point()
bp2.my_print()
输出:
(0,0)
(0,0)
特殊方法(魔术方法)
在类中可以定义一些特殊的方法用来实现特殊的功能,也称为魔术方法。这些方法一般都以双下划线__
开头。
__init__
__init__
又叫构造方法,初始化方法,在调用类名实例化对象时,构造方法会被调用,类名括号()
后的参数会传递给构造方法,对象属性一般在这个方法中定义。
案例:
上面案例中的Point
类实例化后,需要手动创建对象属性x
和y
,这显然容易出错和不规范,正确的做法应该是在构造方法中定义属性x
和y
class Point:
"""
表示平面坐标系中的一个点
"""
name = '点'
def __init__(self, x, y):
self.x = x
self.y = y
def my_print(self):
print('({},{})'.format(self.x, self.y))
def distance(self, p2):
return ((self.x-p2.x)**2 + (self.y-p2.y)**2)**0.5
@classmethod
def base_point(cls):
return cls(0,0)
# 实例化
p1 = Point(1, 2) # 实例化时传递参数给构造方法
p2 = Point(x=3, y=4)
p1.my_print()
p2.my_print()
输出:
(1,2)
(3,4)
__str__
__str__
方法在对象被print
函数打印时被调用,print
输出__str__
方法返回的字符串。
案例:
上面案例中Point
类里的my_print
方法可以去掉,定义一个__str__
方法
class Point:
"""
表示平面坐标系中的一个点
"""
name = '点'
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '({},{})'.format(self.x, self.y)
def distance(self, p2):
return ((self.x-p2.x)**2 + (self.y-p2.y)**2)**0.5
@classmethod
def base_point(cls):
return cls(0,0)
p = Point(2,2)
print(p)
输出:
(2,2)
更多的特殊方法详见官方文档
建议初学者,暂时学习这两个方法就够了。
静态方法
在类中通过装饰器staticmethod
定义静态方法。
静态方法不会接收隐式的第一个参数,它和普通的函数一样,只是被封装到类中。
通过类和对象都可以调用。
案例:¶
在Point类中定义一个静态方法,用来计算两个数的和。
class Point:
"""
表示平面坐标系中的一个点
"""
name = '点'
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '({},{})'.format(self.x, self.y)
def distance(self, p2):
return ((self.x-p2.x)**2 + (self.y-p2.y)**2)**0.5
@classmethod
def base_point(cls):
return cls(0,0)
@staticmethod
def sum(x,y):
return x+y
Point.sum(1,2)
p = Point(1,2)
p.sum(3,4)
输出:
3
7
总结:
当某个函数和对象相关时,这个函数就可以定义为对象方法。
当某个函数和类相关时,这个函数就可以定义为类方法。
某个函数跟类和对象都不相关,但是又想通过类和对象去调用时,就可以定义为静态方法。