对象与类的三大特点:
-
对象(object)是由类(class)产生的。
-
类规划了对象的数据储存方式,这些储存的数据就称为对象的属性。
-
类规划了对象的操作方式,这些操作方式就称为对象的方法。
基本上类就像是对象的设计蓝图,有了类(蓝图)就可以用它来产生或建立对象。同一个类所产生的对象都具有相同的属性及操作方式,就像是同一个模子(蓝图)印出来的。例如,车厂设计好一车型的蓝图(类),然后依此蓝图生产车子(对象),生产出来的车子,其规格和操作方式都一样。
虽然是同一型号(类)的汽车,但每部汽车都是一个独立的对象,出厂时都会赋予一个独立的车体编号。每辆车出厂销售后,其行驶公里数、保养历史等等都不相同,所以相同类别的不同对象其属性值可能不一样。
设计自己的类
定义类要使用 class 关键词,语法如下:
class 类名称(object):
pass
class 后面紧接着是类名,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,如果没有继承,就使用 object 类,这是所有类的最终都会继承的类。不填就代表 object。
在创建对象时,有时必须绑定一些数据,所以通过定义一个特殊的 __ init__ 方法,前后分别有两个下划线。在创建时连带地将资料绑定。语法如下:
def __init__(self, 参数1, 参数2, …):
程序区块
__init__方法的第一个参数永远是 self,表示创建的对象本身,因此,在__init__方法内部,就可以把各种属性绑订到 self,因为 self 就指向创建的对象本身。
有定义__init__方法,在创建对象时,就不能传入空的参数,必须传入与__init__方法匹配的参数,但 self 不需要传,Python解释器会自己处理。
class Person(object):
def __init__(self, age, name):
self.age = age
self.name = name
上例中定义了一个 Person 类,建立对象时,要加入两个参数:年龄及姓名。如:
ben = Person(31, 'Ben')
paul = Person(42, 'Paul')
print(ben.age)
print(ben.name)
建立ben对象之后,就可用变量名称得知 ben 的年龄及名字。像这种我们可以在对象外面读写它们的变量,称之为类的属性。
类的方法和普通的函式相比,在类中定义的函式只有一点不同,就是第一个参数永远是变量 self,并且调用时,不用传递 self 参数。除此之外,类的方法与普通函式没有什么区别,所以你仍然可以用默认参数、可变参数、关键词参数和命名关键词参数。
访问限制
在类的内部,可以有属性和方法,而外部外码可以直接调用,这样就隐藏了内部的复杂逻辑。如果要让内部属性不被外部访问,可以在属性的名称前面加上两个下划线 __,在 Python 中,类中的变量名如果以__开头,就变成一个私有变量(private),只有内部可以访问,外部不能访问。
class Person(object):
def __init__(self, age, name):
self.__age = age
self.__name = name
ben = Person(31, “Ben”)
此时 ben.age 就会出错了,也不能使用 ben.__age。如果要存取 __age,只好再增设两个方法:get_age(), set_age()。如下:
class Person(object):
def __init__(self, age, name):
self.__age = age
self.__name = name
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
继承
要定义一个类,有时候可从某个现有的类继承,新的类称为子类(subclass),而被继承的类称为基类、父类或超类(Base class, Super Class)。
例如,我们定义了一个名为 Animal 的类,有一个 run() 方法可以直接打印:
class Animal(object):
def run(self):
print('Animal is running…')
当我们需要编写 Dog 和 Cat 类时,就可以直接从 Animail 类继承:
class Dog(Animal):
pass
class Cat(Animal):
pass
对于 Dog 来说,Animal 就是它的父类,对于 Animal 来说,Dog 就是它的子类。Cat 和 Dog 类似。
继承的最大好处就是子类拥有了父类的全部功能,由于Animal 实现了run() 方法,因此,Dog 和 Cat 作为它的子类,不用另外定义 run() 方法,就自动拥有了 run() 方法。
dog = Dog()
dog.run()
cat = Cat()
cat.run()
运行结果如下:
Animal is running…
Animal is running…
如果建立对象需要传入参数时:
class Person(object):
def __init__(self, age, name):
self.age = age
self.name = name
class Parent(Person):
def __init__(self, age, name):
Person.__init__(self, age, name)
self.children = []
在 Parent 的__init__ 中调用父类的__init__ 方法,以便获得父类的属性和方法的访问权,而不用重新定义代码。
多态
当子类和父类都存在相同名称的方法(如:run)时,子类的方法会覆盖父类的方法,在代码运行时,总会调用子类的方法,这样就获得了继承的另一个好处:多态。
class Dog(Animal):
def run(self):
print('Dog is running…')
要理解什么是多态,首先要对数据类型再做一点说明。当我们定义一个类的时候,实际上就定义了一种数据类型,与 Python 自带的数据类型: str, list, dict 没什么两样:
a = list()
b = Animal()
c = Dog()
isinstance() 用来判断一个变量是否是某个类型。
>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True
看来 a, b, c 确实对应着 list, Animal, Dog 这3种类型。
>>> isinstance(c, Animal)
True
看来 c 不仅仅是 Dog,也是 Animal!这是因为 Dog 是从 Animal 继承下来的,所以 c 不仅是 Dog 类,也是一种 Animal 类。
要理解多态的好处,看底下的一个函式,它接受一个 Animal 类型的变量:
def run_twice(animal):
animal.run()
当我们传入 Animal 的对象时:
>>> run_twice(Animal())
Animal is running…
当我们传入 Dog 的对象时:
>>> run_twice(Dog())
Dog is running…
像 run_twice() 函式接受一个 Animal 类型的参数,而不用去修改代码就能正常运行,原因就在于多态。