1. 类和实例 在Python中所有的数据类型都可以视为对象,也可以自定义对象。自定义的对象数据类型即面向对象中的类(Class)概念。类是抽象的模板,实例是具体的对象。
类的定义
Python使用class
关键字定义类:
1 2 3 4 5 6 7 8 class Student (object) : def __init__ (self, name, score) : self.name = name self.score = score def print_score (self) : print('%s: %s' % (self.name, self.score))
初始化函数
类中使用__init__
方法实现构造函数的功能,该函数的第一个参数的self
,表示创建的实例本身,调用时传参只需要传入其他参数,不需要传入self
参数,使用__init__
方法则不能传入空参数。
创建实例
1 2 3 4 5 >>> bart = Student('Bart Simpson' , 59 )>>> bart.name'Bart Simpson' >>> bart.score59
类的方法
类内部定义的方法来实现对类内部属性数据的封装,例如例子中的print_score(self)
方法。定义一个类的方法除了第一个参数是self
外,其他与函数的定义一样,调用方法时self
参数不用传入,其他参数正常传入。
方法可以理解为与实例绑定的函数,可以直接访问实例内部的数据。
2. 访问限制 实例的变量以__xxx
双下划线开头,没有以__
双下划线结尾的,则该变量为私有变量,外部一般无法访问。例如:self.__name = name
。
如果变量名是双下划线开头、双下划线结尾,__xxx__
是特殊变量而不是私有变量。私有变量的方式可以避免外部对实例内部数据的修改,可以做参数检查。
如果需要获取或修改内部的数据,可以增加get
和set
的方法。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Student (object) : def __init__ (self, name, score) : self.__name = name self.__score = score def get_name (self) : return self.__name def get_score (self) : return self.__score def set_score (self, score) : if 0 <= score <= 100 : self.__score = score else : raise ValueError('bad score' )
3. 继承和多态 3.1 继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Animal (object) : def run (self) : print('Animal is running...' ) class Dog (Animal) : pass class Cat (Animal) : pass dog = Dog() dog.run() cat = Cat() cat.run() Animal is running... Animal is running...
例子中Dog
和Cat
类继承了Animal
类,因此拥有父类的所有功能。
3.1 方法重写 子类可以对父类的方法进行重写(覆盖),方法名和方法参数都不变,方法的执行体改变。当对子类进行方法调用时是调用子类重写的方法而不是父类的方法。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Dog (Animal) : def run (self) : print('Dog is running...' ) class Cat (Animal) : def run (self) : print('Cat is running...' ) Dog is running... Cat is running...
3.2 多态 在类型系统中,类也可以理解为一种特殊的数据类型,即类是变量类型,而是类的实例是变量名。也可以通过isinstance()
函数来判断某实例是否属于某类。例如:
1 2 3 4 5 6 7 8 9 10 b = Animal() c = Dog() >>> isinstance(b, Animal)True >>> isinstance(c, Dog)True >>> isinstance(c, Animal)True
Dog继承了Animal类,因此Dog的实例的数据类型既可以是Dog(子类),也可以是Animal(父类)
[Dog是Animal的一种],反之则不行。
多态的示例
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 def run_twice (animal) : animal.run() animal.run() >>> run_twice(Animal())Animal is running... Animal is running... >>> run_twice(Dog())Dog is running... Dog is running... >>> run_twice(Cat())Cat is running... Cat is running... class Tortoise (Animal) : def run (self) : print('Tortoise is running slowly...' ) >>> run_twice(Tortoise())Tortoise is running slowly... Tortoise is running slowly...
通过以上示例可以看出,当函数的参数类型是父类,传入参数为子类的时候,调用的是具体传入的子类的方法。即调用方只管调用,不管具体的细节。细节由具体的运行对象决定,并且可以在不改变外部调用代码的情况下,增加调用实现的具体细节,即开闭原则
。
开闭原则
对扩展开放:允许新增Animal
子类;
对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
3.3 鸭子类型 Python是动态语言,在继承上并不需要像静态语言一样传入一个Animal类型或其子类,或者说不要求严格的继承体系。Python的“file-like object
”满足“鸭子类型
”,即一个对象只要看起来像鸭子,走起路来像鸭子,那它就可以被看做是鸭子。
所以并不需要传入一个Animal的类型或子类,只需要传入一个实现了Animal的run()方法的对象即可,例如:
1 2 3 class Timer (object) : def run (self) : print('Start...' )
4. 获取对象信息 4.1 type() 判断对象的类型可以使用type()
函数,对象包括基本的数据类型和函数、类等。例如:
1 2 3 4 5 6 7 8 9 10 11 12 >>> type(123 )<class 'int '> >>> type ('str' ) <class 'str '> >>> type (None) <type (None) 'NoneType '> #函数和类 >>> type (abs) <class 'builtin_function_or_method '> >>> type (a) <class '__main__ .Animal '>
类型比较
可以在if
语句中比较两个对象的数据类型是否一致。
1 2 3 4 5 6 7 8 9 10 >>> type(123 )==type(456 )True >>> type(123 )==intTrue >>> type('abc' )==type('123' )True >>> type('abc' )==strTrue >>> type('abc' )==type(123 )False
type()中常量
types
模块中定义的常量可以用于判断对象是否是函数、生成器等类型。
1 2 3 4 5 6 7 8 9 10 11 12 >>> import types>>> def fn () :... pass ... >>> type(fn)==types.FunctionTypeTrue >>> type(abs)==types.BuiltinFunctionTypeTrue >>> type(lambda x: x)==types.LambdaTypeTrue >>> type((x for x in range(10 )))==types.GeneratorTypeTrue
4.2 isinstance() 继承关系判断
使用isinstance()
函数可以判断一个对象是否是该类型本身或者位于该类型的父继承链上。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 object -> Animal -> Dog -> Husky >>> a = Animal()>>> d = Dog()>>> h = Husky()>>> isinstance(h, Husky)True >>> isinstance(h, Dog)True >>> isinstance(h, Animal)True
以上的继承关系中,子类的类型除了本身的类型外,还可以归属于其祖类(父类及以上)的类型。
基本数据类型的判断
isinstance()
也可以用来判断基本数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 >>> isinstance('a' , str)True >>> isinstance(123 , int)True >>> isinstance(b'a' , bytes)True >>> isinstance([1 , 2 , 3 ], (list, tuple))True >>> isinstance((1 , 2 , 3 ), (list, tuple))True
优先使用isinstance()判断类型
4.3 dir() dir()
函数可以获取一个对象的所有属性和方法,返回一个包含字符串的list。例如:
1 2 >>> dir('ABC' )['__add__' , '__class__' ,..., '__subclasshook__' , 'capitalize' , 'casefold' ,..., 'zfill' ]
dir()
可以结合getattr()
、setattr()
以及hasattr()
使用,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >>> class MyObject (object) :... def __init__ (self) :... self.x = 9 ... def power (self) :... return self.x * self.x... >>> obj = MyObject()>>> hasattr(obj, 'x' ) True >>> obj.x9 >>> hasattr(obj, 'y' ) False >>> setattr(obj, 'y' , 19 ) >>> hasattr(obj, 'y' ) True >>> getattr(obj, 'y' ) 19 >>> obj.y 19
此类函数是在未知对象内部数据时使用,例如从文件流中读取图像,先判断是否有read()
方法,如果有则可以读取。
1 2 3 4 def readImage (fp) : if hasattr(fp, 'read' ): return readData(fp) return None
5. 实例属性和类属性 Python中除了给实例绑定属性外,还可以给类绑定属性,即类属性。类属性归类所有,但该类的所有实例都可以访问到类属性。实例属性的优先级比类属性高,类属性和实例属性一般不使用相同名字,如果同名则实例属性会屏蔽掉类属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>> class Student (object) :... name = 'Student' def __init__ (self, name) : self.name = name ... >>> s = Student() >>> print(s.name) Student >>> print(Student.name) Student >>> s.name = 'Michael' >>> print(s.name) Michael >>> print(Student.name) Student >>> del s.name >>> print(s.name) Student
文章参考:廖雪峰Python教程