当前位置:   article > 正文

【Python学习手册】类与对象详解(值得收藏的学习手册)_python类是一份 说明书 类对象

python类是一份 说明书 类对象

python专栏:【全网最强】Python系统学习手册

目录

python专栏:【全网最强】Python系统学习手册

一、类与对象

1.面向对象

2.动态类型语句

3.类的定义

4.类的成员

成员变量

成员方法

成员的动态性

5.构造方法

6.实例方法

7.动态增删改

8.类的继承

二、方法

1.类方法与静态方法

2.函数装饰器

3.一般的函数装饰器

三、成员变量

四、隐藏和封装

封装的目的

良好的封装需要从两个方面考虑:

使用property定义属性

五、类的继承

重写父类方法

调用被重写的方法

使用super函数

六、多态

类型检查函数

七、枚举类

枚举类的派生类


一、类与对象

用于描述复杂的对象,包含很多属性和方法。

Class:某一些有共同特征的对象的抽象,是某种概念。

对象(Object:也称为实例(instance),是具体存在的实体,每个实体具备一定的特征。对象之间有相同点也有不同点。

1.面向对象

  • 面向对象(Object-Oriented Programming)是一种编程范式,是模块化设计的重要方法。
  • 世界由各种对象组成,不同对象之间可以相互作用和通讯。
  • 描述对象特征的信息称为属性(Attribute
  • 存取属性的函数称为方法(Method
  • 具有相同属性与方法的对象(Object),属于同一个类别,称之为一个类(Class,其中的一个对象为该类的实例(Instance

2.动态类型语句

  • python是动态类型语言,不止属性值可以变化,对象的所属类别、方法都可以变化。
  • 一个对象可以动态转换为不同的类,具有不同的属性和方法。属性和方法可以动态的添加和删除,具备高度的灵活性

3.类的定义

  •   class 类名(基类):

              零到多个成员变量...

              零到多个执行语句...

              零到多个成员方法...

  • 类名只要是合法的标识符即可,合法的变量名可以作为类名。推荐使用大写字母开头,下划线分隔的一系列有意义的单词作为类名。
  • 注意冒号和缩进的规范
  • 类定义用class关键字,函数定义用def关键字,类中的方法(成员函数)也用def定义。
  • 类中的成员变量和方法的顺序没有影响,各个成员之间可以相互调用。

4.类的成员

成员变量

  • 类变量:属于类本身,用于定义该类整体的状态数据。
  • 实例变量:属于该类的某一个对象,用于定义某个对象所包含的状态数据。

成员方法

  • 类方法:用于定义一个类的整体行为,不随对象变化,不绑定具体对象。
  • 实例方法:用于定义该类的某个对象的行为或功能,只对具体对象。在类中定义的方法默认为实例方法。

成员的动态性

  • 类变量可以在任何地方增、删、改:在类中为新变量赋值,可以自动增加类变量。用del删除类变量。
  • 对实例变量也类似,也可以在任何地方动态增、删、改。对不存在的赋值就是动态增加,对已有的变量del就是动态删除。

5.构造方法

  • 定义了一个类,构造方法就是从类定义出发,生成一个实例。构造方法返回该类的对象。
  • python的构造方法有特殊的名字:__init__,必须用这个名字不能随便换!注意init前后都是连续的双下划线。
  • 构造方法的第一的参数是self,表示被构造的实例对象。一般会在构造方法中为self.varname赋值,将对象装配好。
  • 构造方法结束时不需要使用return,它会自动将装配好的对象也就是self当作返回值。
  • python规定,每一个类,必须有一个构造方法。如果你没有定义,python会自动生成一个只有参数self的空的构造方法。

6.实例方法

  • 在类中定义的方法,python会自动为它们绑定第一个参数,一般这个参数约定俗成的命名为self。这个参数总是指向调用该方法的对象。
  • 由于实例方法的第一个参数self自动绑定,传参的时候不需要传它。self指向的对象是不确定的,但对象的类型是确定的。
  • python的类和对象,类似于c++的命名空间,因此在调用类和对象的方法时,一定要使用类.和对象.的形式。
  • 对象.方法(参数)调用实例方法,用类.方法(参数)的方式调用类方法。
  • 也可以用类.方法(对象,参数)的方式对某一个实例调用方法。这种方式称为未绑定方法,用户需要指定一个实例对象。
  1. # 第一个面向对象程序,定义人类:一个关于人的类
  2. class Person:
  3. ''' 定义一个人 '''
  4. def __init__(self,name,age=0):
  5. ''' 构造方法,定义与生俱来的两个属性,姓名和年龄 '''
  6. self.name = name
  7. self.age = age
  8. def birthday(self):
  9. ''' 定义实例方法,过生日 ''' # 定义一个实例方法
  10. self.age += 1
  11. print('%s%d岁啦,生日快乐!!!' % (self.name, self.age))
  12. def open_mouth(self):
  13. ''' 张嘴 '''
  14. print('%s张嘴。' % self.name)
  15. def chew(self):
  16. ''' 咀嚼 '''
  17. print('%s嚼一嚼。' % self.name)
  18. def eat(self):
  19. ''' 吃饭 ''' # 定义一个实例方法,依赖其他方法
  20. self.open_mouth() # 在类定义中调用其它的方法,使用self.xxx的方式
  21. self.chew()
  22. a = Person('张三') # 使用构造方法,由于第一个self参数自动绑定,只需传入后面的参数
  23. a.name
  24. a.age
  25. a.birthday() # 调用过生日方法,第一个参数自动绑定,不需要传参了
  26. a.birthday()
  27. a.age
  28. a.eat()
  29. # python中,也可以用 类.方法(对象) 的方式来调用方法,就是看着啰嗦
  30. Person.chew(a)

7.动态增删改

  1. # 实例变量的动态增删改
  2. # 增
  3. a.nickname = '小明'
  4. print(a.nickname)
  5. # 改
  6. a.nickname = '小麻'
  7. print(a.nickname)
  8. # 删
  9. # del a.nickname
  10. print(a.nickname) # 删除之后,这个成员变量不存在了,会报错
  11. # 类变量的动态增删改
  12. # 增
  13. Person.charactor = '勇敢'
  14. print(Person.charactor)
  15. print(a.charactor) # 每个实例都能够调用类变量
  16. # 改
  17. Person.charactor = '智慧'
  18. print(Person.charactor)
  19. print(a.charactor)
  20. # 删
  21. # del Person.charactor
  22. print(Person.charactor)

8.类的继承

  • 和其它面向对象语言一样,python也支持类的继承。
  • 继承就是:从另一个类中全盘拷贝变量和方法,实现代码复用。被继承的类称为基类,继承者叫派生类。
  • 派生类可以在基类的基础上增加自己的变量和方法,基类已经有的不需要再写一遍。对派生类的操作不会影响到基类。
  • 继承操作只需要在class 类名后面添加(基类)即可。
  1. class Student(Person):
  2. ''' 定义类:学生,继承 “人”这个类别 '''
  3. def __init__(self,name,age=0,school='北师大'):
  4. ''' 初始化一个“学生” '''
  5. Person.__init__(self,name,age) # 先使用基类的初始化方法
  6. self.school = school # 在此基础上添加
  7. def gotoschool(self): # 新增一个成员方法
  8. ''' 上学函数 '''
  9. print('%s去%s上学啦!' % (self.name, self.school))
  10. b = Student('韩梅梅')
  11. b.gotoschool()

二、方法

1.类方法与静态方法

  • python支持定义类方法和静态方法,定义的方式就是使用函数装饰器。
  • 使用@classmethod修饰的是类方法自动绑定类方法的第一个参数cls
  • 使用@staticmethod修饰的是静态方法。静态方法不会自动绑定参数
  • 类方法可以访问类变量,一般用它来对类变量进行增删改。由于python的动态性,这些事情在哪儿都能做,但集中到类方法里面做更方便管理。
  • 静态方法其实单独定义的函数没有区别。只有名字和当前类有关。

2.函数装饰器

  • python的语法中,@符号作为函数装饰器使用,它的作用是引用已有的函数,修饰其它的函数。
  • 所谓函数装饰器,相当于给函数“穿马甲”,添加或者改变原有函数的功能。
  • python的函数装饰器语法使它能简便优美的实现很多功能,比如类方法,静态方法,以及即时编译(jit)
  1. class Mosquito:
  2. '''
  3. 蚊子类。目前我们只关心蚊子的行为,不关心具体的蚊子,所以没有构造方法。这个类不会产生实例对象。
  4. '''
  5. # 使用@classmethod修饰类方法,自动绑定第一个参数,也就是当前类cls
  6. @classmethod
  7. def fly(cls):
  8. '''
  9. 飞行方法,蚊子都会飞。
  10. '''
  11. print('这个类是会飞的: ', cls)
  12. # 使用@staticmethod修饰静态方法
  13. @staticmethod
  14. def sting(p):
  15. '''
  16. 叮咬方法,我们关心谁被蚊子叮了,并不关心叮人的具体是哪只蚊子。这个方法的参数是人类对象。
  17. '''
  18. print(p.name + '被蚊子叮了。')
  19. Mosquito.sting(a)
  20. Mosquito.fly()

3.一般的函数装饰器

  • 一般的函数装饰器,可以理解为函数套函数。在函数func()的前面加上@gfunc,则产生的函数实际上是gfunc(func())
  • gfunc()必须是一个以函数为参数的函数。
  • func()是被装饰得函数,装饰前后,函数的参数不发生变化,返回值可能发生变化,也可能不变。
  • 装饰后函数的返回值,事实上就是gfunc()的返回值。
  • 函数装饰器作用:用来对一个函数进行包装,在其前后加入通用的辅助功能:

           1)在前面,加上权限检查,数据合理性与一致性检查,等必要的前处理。

           2)在后面,加上记录日志,保存结果,统计绘图,等必要的后处理。

           3)适用于前后处理可以通用,中间的函数算法可以灵活替换的场景。

           4)可以使用*args代替不定个数的参数,增加装饰器的通用性。

  1. def pre_post(func):
  2. '''
  3. 用装饰器实现通用的前后处理 '''
  4. def pre_post_f(*args):
  5. '''
  6. 实际进行前后处理操作的函数 '''
  7. # 用一句话代替前后处理过程
  8. print('前处理......')
  9. f = func(*args)
  10. print('后处理......')
  11. # 内层函数的返回值为func的返回值
  12. return f
  13. # 外层函数,把包装好的内层函数作为返回值
  14. return pre_post_f
  15. # 用通用前后处理函数来包装一个求平均值的函数
  16. @pre_post
  17. def mymean(x):
  18. return sum(x)/len(x)
  19. # 运行mymean函数,实际上运行的是含有前后处理的,用 pre_post包装好了的函数
  20. x = list(range(10))
  21. print(mymean(x))

三、成员变量

  • python类的成员变量分为类变量实例变量,可以动态增删改。
  • 变量和实例变量有不同的用途、定义方式、使用方式。
  • 类变量在类的命名空间内定义
  • 实例变量在构造方法内定义
  • 类变量可以通过类名.变量实例名.变量两种方式访问
  • 实例变量只能通过实例名.变量访问和修改
  1. # 中国城市信息列表
  2. class City:
  3. # 定义类变量
  4. country = 'China' # 中国城市
  5. num_of_cities = 0 # 城市总数
  6. def __init__(self, name, lon, lat, postcode):
  7. self.name = name
  8. self.lon = lon
  9. self.lat = lat
  10. self.postcode = postcode
  11. City.num_of_cities += 1
  12. def print_city(self):
  13. # 使用类变量一定要带上类名,下面这句话会报错
  14. # print('Country: %s' % contry)
  15. # 下面这些是正确的
  16. print('Country: %s' % City.country)
  17. print('Total number of cities: %d' % City.num_of_cities)
  18. print('City name: %s' % self.name)
  19. print('Longitude: %7.3f' % self.lon)
  20. print('Latitude: %7.3f' % self.lat)
  21. print('Post code: %d' % self.postcode)
  • 通过实例BJ修改类变量country,然后再从类中访问,发现变量并没有被修改。
  • 由于python语言的动态性,用赋值=修改变量的时候,事实上并没有修改类变量country,而是在实例BJ里面新建了一个实例变量,也叫country
  • 因此,修改类变量最好是通过类来修改。通过实例只能读不要增删改!
  1. BJ = City('北京',116.408,39.904,100000)
  2. BJ.print_city()
  3. SH = City('上海',121.445,31.213,200000)
  4. SH.print_city()
  5. GZ = City('广州',113.265,23.108,510000)
  6. GZ.print_city()
  7. # 访问类变量
  8. # 通过类访问
  9. print(City.country)
  10. # 通过类修改
  11. City.country = '中国'
  12. print(City.country)
  13. # 通过实例访问
  14. print(BJ.country)
  15. # 通过实例修改
  16. BJ.country = 'BJ中国' # 通过实例BJ修改
  17. # 从类中访问,发现类变量没有被改过!
  18. print(City.country)
  19. # 从实例BJ中访问,发现被改了
  20. print(BJ.country)
  21. # 从其它实例中访问,发现还是没有被修改
  22. print(SH.country)
  23. # 通过dir发现BJ中新建了一个实例变量,也叫country!
  24. dir(BJ)

四、隐藏和封装

面向对象的三大特征封装Encapsulation)、继承和多态

封装指的是将对象的状态信息隐藏在内部,不允许外部直接访问。通过该类提供的方法,完成数据完整性、一致性、权限检查等操作后才允许访问和修改。

双下划线__开头的是隐藏变量和方法。

类变量、类方法、实例变量、实例方法,都是可以隐藏的。也可以通过“作弊”的方式访问到隐藏变量和方法。但是非常不推荐这样做!

封装的目的

  • 隐藏类的实现细节。限制不合理的访问和修改。
  • 通过数据检查保证对象信息的完整性。
  • 便于集中修改,提高代码的可维护性。

良好的封装需要从两个方面考虑:

  • 该藏的藏起来:隐藏对象的属性和实现细节,不允许外部直接访问
  • 该露的露出来:编写相应的方法操作这些隐藏属性,通过在方法中的各种检查来保证访问和操作的安全
  1. # 中国城市信息列表,使用封装来增加安全性
  2. class City:
  3. # 定义类变量,所有的类变量都是隐藏变量
  4. # 中国城市
  5. __country = 'China'
  6. # 城市总数
  7. __num_of_cities = 0
  8. def __num_plusone():
  9. ''' 隐藏方法,让城市的总数加一。 '''
  10. City.__num_of_cities += 1
  11. def __init__(self, name, lon, lat, postcode):
  12. ''' 构造方法,所有的实例变量都是隐藏变量。 '''
  13. self.__name = name
  14. self.__lon = lon
  15. self.__lat = lat
  16. self.__postcode = postcode
  17. City.__num_plusone()
  18. def print_city(self):
  19. ''' 把实例对象和类的信息打印出来。 '''
  20. print('Country: %s' % City.__country)
  21. print('Total number of cities: %d' % City.__num_of_cities)
  22. print('City name: %s' % self.__name)
  23. print('Longitude: %7.3f' % self.__lon)
  24. print('Latitude: %7.3f' % self.__lat)
  25. print('Post code: %d' % self.__postcode)
  26. # 读取类和对象的属性
  27. def get_name(self):
  28. return self.__name
  29. def get_lat(self):
  30. return self.__lat
  31. def get_lon(self):
  32. return self.__lon
  33. def get_coordinate(self):
  34. return self.__lat, self.__lon
  35. def get_postcode(self):
  36. return self.__postcode
  37. def get_country():
  38. return City.__country
  39. def get_num_of_cities():
  40. return City.num_of_cities
  41. # 没有写入方法,对象一旦创建就不能修改,类的属性只能通过内部的方法修改

看起来python的隐藏方法是有效的。然而,事实上python并没有真正的隐藏机制,它只是把被隐藏的变量给改了名字。

_类名__变量或方法名的方式,可以访问到被隐藏的东西。但是平时不建议这样做。

  1. # 创建一些对象
  2. BJ = City('北京',116.408,39.904,100000)
  3. BJ.print_city()
  4. SH = City('上海',121.445,31.213,200000)
  5. SH.print_city()
  6. GZ = City('广州',113.265,23.108,510000)
  7. GZ.print_city()
  8. # 直接访问实例变量和类变量,会报错,隐藏变量从类定义的外部,既不能读也不能写
  9. #print(City.country)
  10. print(BJ.__name)
  11. # 通过get方法间接访问类变量和实例变量
  12. print(City.get_country())
  13. print(BJ.get_name())
  14. # 作弊式访问隐藏变量
  15. print(BJ._City__name)
  16. # 作弊式修改隐藏变量
  17. BJ._City__name = 'Beijing'
  18. print(BJ._City__name)

使用property定义属性

  • 使用双下划线隐藏变量和方法,实现封装,然后通过一系列getset方法实现。
  • 可以通过property()函数,把几个变量攒起来,变成虚拟属性。

property()函数的语法格式如下:

        property(fget = None, fset = None, fdel = None, doc = None)

  • property()函数可以传入四个参数,分别代表读方法(fget)、写方法(fset)、删除方法(fdel)和文档字符串(doc
  • 传入1个参数,表示只读(只有fget
  • 传入2个参数,表示读写(有fgetfset
  • 传入3个参数,表示读写删
  • 传入4个参数,表示读、写、删、文档齐全
  1. # 中国城市信息列表,使用property定义属性
  2. class City:
  3. # 定义类变量,所有的类变量都是隐藏变量
  4. # 中国城市
  5. __country = 'China'
  6. # 城市总数
  7. __num_of_cities = 0
  8. def __num_plusone():
  9. ''' 隐藏方法,让城市的总数加一。 '''
  10. City.__num_of_cities += 1
  11. def __init__(self, name, lon, lat, postcode):
  12. ''' 构造方法,所有的实例变量都是隐藏变量。 '''
  13. self.__name = name
  14. self.__lon = lon
  15. self.__lat = lat
  16. self.__postcode = postcode
  17. City.__num_plusone()
  18. def print_city(self):
  19. ''' 把实例对象和类的信息打印出来。 '''
  20. print('Country: %s' % City.__country)
  21. print('Total number of cities: %d' % City.__num_of_cities)
  22. print('City name: %s' % self.__name)
  23. print('Longitude: %7.3f' % self.__lon)
  24. print('Latitude: %7.3f' % self.__lat)
  25. print('Post code: %d' % self.__postcode)
  26. # 读取类和对象的属性
  27. def get_name(self):
  28. return self.__name
  29. def get_lat(self):
  30. return self.__lat
  31. def get_lon(self):
  32. return self.__lon
  33. def get_coordinate(self):
  34. return self.__lat, self.__lon
  35. def get_postcode(self):
  36. return self.__postcode
  37. def get_country():
  38. return City.__country
  39. def get_num_of_cities():
  40. return City.num_of_cities
  41. # 写入对象的属性(类的属性不能直接写)
  42. def set_name(self, name):
  43. self.__name = name
  44. def set_lat(self, lat):
  45. self.__lat = lat
  46. def set_lon(self, lon):
  47. self.__lon = lon
  48. def set_coordinate(self, coordinate):
  49. self.__lat, self.__lon = coordinate
  50. def set_postcode(self, postcode):
  51. self.__postcode = postcode
  52. # 定义coordinate属性的删除操作
  53. def del_coordinate(self):
  54. self.__lat, self.__lon = 0, 0
  55. # 用property定义属性
  56. coordinate = property(get_coordinate, set_coordinate, del_coordinate, \
  57. '城市的经纬度坐标,北纬在前,东经在后,南和西方向为负值')
  • property所定义的属性,并不是类定义中真实存在的类或实例变量,而是用已有变量合成出来的。
  • 这种属性被称为计算属性,它并不真正的存储什么东西,而是通过类和对象的状态计算出来的。
  • 对计算属性赋值,python会自动使用set方法更新相应的类或对象内部变量。
  1. # 创建一些对象
  2. BJ = City('北京',116.408,39.904,100000)
  3. BJ.print_city()
  4. SH = City('上海',121.445,31.213,200000)
  5. SH.print_city()
  6. GZ = City('广州',113.265,23.108,510000)
  7. GZ.print_city()
  8. # 访问coordinate的说明文档
  9. help(City.coordinate)
  10. # 访问coordinate的说明文档,也可以直接取得成员的__doc__字符串
  11. print(City.coordinate.__doc__)
  12. # 访问coordinate属性
  13. print(BJ.coordinate)
  14. # 事实上是通过get方法访问的
  15. # 直接对coordinate属性赋值
  16. BJ.coordinate = 39, 116
  17. print(BJ.coordinate)
  18. # 事实上是通过set方法修改的,是安全的
  19. # 直接访问隐藏的属性,例如lat,还是不行的
  20. print(BJ.__lat)
  21. # 通过get方法访问lat,确认lat也被修改了
  22. print(BJ.get_lat())

五、类的继承

  • 继承就是子类全盘拷贝父类的成员(变量和方法),并在此基础上定义自己的成员。
  • python支持多继承。但多重继承会令编程复杂度极大增加最好还是用单继承,然后添加需要的变量和方法。
  • 多重继承,指一个子类有多个直接的父类。该子类会得到所有父类的所有方法。
  • 如果父类中有同名方法,则前面的父类中的方法会“遮蔽”后面父类的同名方法
  • 看来flyfire两个函数都在。Plane的构造函数优先,Weapon的构造函数被覆盖了。
  1. # 多重继承例子
  2. # 定义飞机类
  3. class Plane:
  4. def __init__(self, name, speed = 2000):
  5. self.name = name
  6. self.speed = speed # 飞行速度
  7. def fly(self):
  8. print('%s是飞机,可以飞!' % self.name)
  9. # 定义武器类
  10. class Weapon:
  11. def __init__(self, name, attack_p = 500):
  12. self.name = name
  13. self.attack_p = attack_p # 攻击力点数
  14. def fire(self):
  15. print('%s是武器,开火!!!' % self.name)
  16. # 定义战斗机类,继承飞机和武器
  17. class Fighter(Plane, Weapon):
  18. pass # 什么都不做,先看看里面有什么
  19. # 定义战斗机类Fighter的实例
  20. f = Fighter('歼20')
  21. # 看看里面有什么
  22. print(f.name)
  23. print(f.speed)
  24. #print(f.attack_p) # 攻击力属性丢了!
  25. f.fly()
  26. f.fire()

重写父类方法

  • 有时候子类不仅仅是需要在父类的基础上增加新的方法,还需要改写父类已有的方法。
  • 例如客机、直升机、战斗机都是飞机,但它们的飞行方式不同。直升机能垂直起降,客机都是固定翼飞机不能垂直起降。战斗机一般不能垂直起降,但有一类特殊的飞机(F35B,海鹞,雅克38,雅克141)虽然是固定翼的但也能垂直起降。现代战斗机一般都能超音速飞行。
  • 如果子类的同名方法和父类不一样,就需要重写(override)该方法
  1. # 重写父类方法例子
  2. class Aircraft():
  3. ''' 飞行器,包括热气球、飞艇、各种固定翼和旋翼飞机。在大气层内飞行。 '''
  4. def __init__(self, name, maxspeed = 100):
  5. self.name = name
  6. self.maxspeed = maxspeed # 飞行速度
  7. def fly(self):
  8. print('%s是飞行器(aircraft),可以在大气层内飞行。' % self.name)
  9. class Plane(Aircraft):
  10. ''' 固定翼飞行器(Fixed-wing aeroplane),一般简称为飞机(plane),利用固定的机翼产生的升力在大气层内飞行。 '''
  11. def fly(self): # 重写父类的方法
  12. print('%s是固定翼飞行器(Fixed-wing aeroplane),利用固定的机翼产生的升力在大气层内飞行。' % self.name)
  13. class Helicopter(Aircraft):
  14. ''' 直升飞机(Helicopter),利用旋转的机翼产生的升力在大气层内飞行。 '''
  15. def fly(self): # 重写父类的方法
  16. print('%s直升飞机(Helicopter),利用旋转的机翼产生的升力在大气层内飞行。' % self.name)
  17. # 定义固定翼飞机和直升飞机的实例
  18. a = Plane('C919')
  19. a.fly()
  20. b = Helicopter('直20')
  21. b.fly()

调用被重写的方法

有时候我们需要在子类中调用被重写的方法,只需要指定父类的名字就可以了。这个时候需要显示的给它传入self参数。

  1. # 定义战斗机类,与之前不同的是,这次不使用多重继承,同时测试调用被重写的父类方法
  2. class Fighter(Plane):
  3. def __init__(self, name, maxspeed = 2000, attack_p = 500):
  4. self.name = name
  5. self.maxspeed = maxspeed # 飞行速度
  6. self.attack_p = attack_p # 攻击力点数
  7. # 添加新的方法:开火
  8. def fire(self):
  9. print('%s是武器,开火!!!' % self.name)
  10. # 重写父类方法
  11. def fly(self):
  12. # 首先调用父类的同名方法,注意要给这个方法传入self参数
  13. Plane.fly(self)
  14. # 后面是新添加的内容
  15. print('%s是战斗机,可以高速飞行。' % self.name)
  16. # 定义一个实例,看看fly方法执行的结果如何
  17. c = Fighter('歼20')
  18. c.fly()

使用super函数

  • python还支持使用super函数直接调用父类中的方法。我们可以用super函数重写Figher类。
  • super函数其实是super类的构造函数,它能自动绑定self,因此不需要传入self参数了。
  1. # 定义战斗机类,使用super函数
  2. class Fighter(Plane):
  3. def __init__(self, name, maxspeed = 2000, attack_p = 500):
  4. # 使用super函数调用父类的构造方法
  5. super().__init__(name, maxspeed)
  6. self.attack_p = attack_p # 攻击力点数
  7. # 添加新的方法:开火
  8. def fire(self):
  9. print('%s是武器,开火!!!' % self.name)
  10. # 重写父类方法
  11. def fly(self):
  12. # 使用super函数调用父类的方法
  13. super().fly()
  14. # 后面是新添加的内容
  15. print('%s是战斗机,可以高速飞行。' % self.name)
  16. # 定义一个实例,看看fly方法执行的结果如何,和上面是一样的
  17. c = Fighter('歼20')
  18. c.fly()
  19. # 看看super函数的帮助,发现它其实是super类的构造方法
  20. help(super)

六、多态

  • python属于弱类型、动态类型语言,python的变量本身没有声明类型。变量指向什么类型,它就是什么类型。
  • 事实上,多态是一种灵活的编程机制,会根据传入的对象自动执行不同的行为。以飞行器类为例,会根据不同的类型选择不同的方式执行fly方法。
  • 多态在编写大型程序的时候非常有用。用户只需给出方法的名称,如何执行完全取决于对象本身。因此能省略大量的if或者switch之类的流程控制。
  1. # 定义实例
  2. a = Plane('C919')
  3. a.fly()
  4. # 同一个变量,换一个类
  5. a = Helicopter('直20')
  6. a.fly()
  7. # 再换一个类,同样是执行fly方法,执行方式不同
  8. a = Fighter('歼20')
  9. a.fly()

类型检查函数

  • 多态的使用非常方便,但也有可能引入bug,例如调用不存在的方法,或者方法的行为在意料之外。
  • 为了保证程序质量,有时候需要有必要的类型检查,然后再调用方法。
  • issubclass(cls,class_or_turple): 检测类的继承关系:检查cls是否为有一个类或者元组中所包含的多个类中任意类的子类。
  • isinstance(cls,class_or_turple): 检测类和对象的关系:检查cls是否为有一个类或者元组中所包含的多个类中任意类的子类。
  1. # 用issubclass检测类的继承关系
  2. # Fighter是Plane的子类
  3. print(issubclass(Fighter,Plane))
  4. # Fighter不是Helicopter的子类
  5. print(issubclass(Fighter,Helicopter))
  6. # 使用元组作为第二个参数,只要符合其中一个就是True
  7. print(issubclass(Fighter,(Plane,Helicopter)))
  8. # 用isinstance检测类和对象的关系
  9. a = Fighter('歼20')
  10. # 是Fighter对象吗?
  11. print(isinstance(a,Fighter))
  12. # 是Plane对象吗?
  13. print(isinstance(a,Plane))
  14. # 是Helicopter对象吗?
  15. print(isinstance(a,Helicopter))
  16. # 使用元组作为第二个参数,只要符合其中一个就是True
  17. print(isinstance(a,(Plane,Helicopter)))

七、枚举类

  • 枚举类型是一种特殊的数据类型,其中的对象数量是有限个。
  • python一般是通过Enum类的构造函数来定义枚举类enumeration [ɪˌnuːməˈreɪʃn]
  • 构造函数Enum()的第一个参数是类名,第二个参数是一个元组,列出所有枚举值。
  • 枚举类的每个成员有namevalue两个属性。name是成员的名称,value是成员的枚举值(顺序编号,从1开始,记住是从1开始!)
  1. # 定义枚举类,一年四季
  2. import enum
  3. S = enum.Enum('Season',('spring','summer','fall','winter'))
  4. # 访问枚举类
  5. # 打印成员
  6. print(S.spring)
  7. # 打印成员的name属性
  8. print(S.spring.name)
  9. # 打印成员的value属性
  10. print(S.spring.value)
  11. # 通过变量名来访问成员(注意是用方括号,类似于访问字典类型)
  12. print(S['spring'])
  13. # 通过枚举值来访问成员(注意是用圆括号,编号从1开始,不是从0开始!)
  14. print(S(1))
  15. # 枚举类自带的__members__属性,将枚举类转换为字典(有序字典)
  16. print(S.__members__)

枚举类的派生类

  • 有时候单纯的枚举类功能太单一了,和只读的有序字典差不多。
  • 可以从Enum类派生出我们自己的类,添加我们自己需要的方法。
  1. class Season(enum.Enum):
  2. # 为序列指定value值
  3. spring = '春'
  4. summer = '夏'
  5. fall = '秋'
  6. winter = '冬'
  7. def info(self):
  8. print('这是一个代表季节%s的枚举' % self.value)
  9. # 通过成员名访问
  10. print(Season['spring'])
  11. # 通过枚举值来访问
  12. print(Season('春'))
  13. # 使用info方法
  14. Season['summer'].info()
  15. # 使用__members__遍历有序字典
  16. for name, member in Season.__members__.items():
  17. print(name, ':', member, ',', member.value)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/829580
推荐阅读
相关标签
  

闽ICP备14008679号