在Python中,类也是一种数据类型。大多数情况下,类(class)和类型(type)是一个概念。
在类中定义的函数被成为方法(method)。
方法的第一个参数我们通常约定命名为self,self指代的是被这个方法所操作的对象。
在调用方法时,我们不需要显式传入参数给self,点“.”符号已经自动的帮助我们填充了这个参数。例如u.norm()相当于Vector.norm(u)。
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def norm(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
u = Vector(3,4)
__init__方法被称为初始化方法(initializer)。这类开头和结尾是双下划线的方法被称为魔法方法。当我们定义自己的方法时,要避免使用这种定义方式,以避免引起混淆。
通常魔法方法不会被显式的调用,而是通过其他的一些方式被隐式调用。
类的封装以及公有接口
封装有两个不同但相近的含义。第一是将数据、方法等打包成一个整体,在python中由class完成。第二是强调了类的外部和内部边界,特别是哪些部分是可以通过对象访问的,哪些是不可以的。
通常这意味着将属性划分为公有的public和私有的private。不过在python中没有明确定义public和private如何划分,换言之一切本质上都是公有的(public)。
在python中,我们通常将使用单下划线开头的属性(包括方法、变量等等)定义为私有private属性。
class Diary:
def __init__(self, title):
self.title = title
self._entries = []
def addentry(self, entry):
self._entries.append(entry)
def _lastentry(self):
return self._entries[-1]
在上述代码中,类Diary中变量title,addentry方法都属于public;_entries列表和_lastentry方法都属于private。
继承与“是一个”关系
继承的作用是将多个类的公共特性抽取出来,单独组成一个父类。举个例子,科比是一个篮球运动员,同时他是一个美国人,是一个人,是一个哺乳动物。。。同时姚明、易建联都是篮球运动员,同时他们也都是人。因此我们就可以将他们作为“人”的那一部分属性抽取出来,构成一个Human类,让他们继承Human,以此减少代码量以及降低出现bugs的可能性。
举个栗子:
class Triangle:
def __init__(self,points):
self._sides = 3
self._points = list(points)
if len(self._points) != 3:
raise ValueError("Wrong number of points")
def sides(self):
return 3
def __str__(self):
return "I'm a triangle."
class Square:
def __init__(self,points):
self._sides = 4
self._points = list(points)
if len(self._points) != 4:
raise ValueError("Wrong number of points.")
def sides(self):
return 4
def __str__(self):
return "I'm so square."
class Triangle和class Square有一些共同特点:都具有边和点,都有查找边数和输出方法。因此我们可以将这些特性抽象出来,构成Polygon类,如下所示:
class Polygon:
def __init__(self, sides, points):
self._sides = sides
self._points = list(points)
if len(self._points) != self._sides:
raise ValueError("Wrong number of points.")
def sides(self):
return self._sides
class Triangle(Polygon):
def __init__(self,points):
Polygon.__init__(self,3,points)
def __str__(self):
return "I'm a triangle."
class Square(Polygon):
def __init__(self,points):
Polygon.__init__(self,4,points)
def __str__(self):
return "I'm so square."
他们继承的类称为“超类”(superclass)。
当我们创建一个子类的新的实例时,超类的初始化方法不会被自动调用。因此我们需要手动进行初始化。即在子类的初始化方法中调用超类的初始化方法。
如上图代码所示的使用“superclassname.__init__(self,sides,points)”初始化,即直接用类名进行初始化,在单继承时没有问题,但是在多继承时,会涉及到查找顺序,重复调用等种种问题。我们还可以使用“super().__init__()”
class Polygon:
def __init__(self, sides, points):
self._sides = sides
self._points = list(points)
if len(self._points) != self._sides:
raise ValueError("Wrong number of points.")
def sides(self):
return self._sides
class Triangle(Polygon):
def __init__(self,points):
super().__init__(3,points)
def __str__(self):
return "I'm a triangle."
class Square(Polygon):
def __init__(self,points):
super().__init__(4,points)
def __str__(self):
return "I'm so square."
继承意味着“是一个”
例如B继承了A,那么B是一个A,它们之间的关系要清楚。