面向对象
面向对象的三大特性
一、多态
- python是弱类型语言,不需要在变量的声明中明确变量的数据类型
- python没有多态,但又处处是多态。
- python有鸭子类型
- 看着是鸭子,就是鸭子
- 不同的类中存在相同的方法,这些方法的功能也类似,那么这些类就被称为鸭子(例如:字符串类、列表类、字典类等都具有index属性)
二、继承
- 继承分为单继承和多继承,多继承遵循的是从左往右的原则,左侧优先。
- python3中的继承原则采用的是广度优先原则,继承类都是新式类;python2采用的是深度优先,继承的都是经典类
- python3中的子类和派生类只能继承父类中的公有变量和方法,不能继承父类中的私有方法和私有动态变量、私有静态变量。
三、封装
- “封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类)
- 封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。
- 封装的好处
- 封装后外部的使用者只需要传递出响应的参数,就可以通过对象调用内部的数据和方法,而不需要知道内部的实现方式
- 封装后如果需要新的方法,可以在类中添加。
- 广义的封装:给对象封装属性,给类中封装静态字段、属性以及构造方法。
- 狭义的封装:私有制、私有成员
- 私有成员:静态私有字段、私有方法、私有属性
- 只能在本类中被调用
四、什么是面向对象
- 什么时候用:当需要处理复杂的角色之间关系的时候(代码量大,功能多的时候),例如:
- qq、好友、陌生人、群、组
- 复杂的电商系统
- 公司或者学校的人事管理/功能的系统
- 为什么用:
- 在处理复杂的角色关系的时候可以使得不同类型角色之间隔离开来。代码的清晰度更高,无论是开发者还是调用者
都能明确的分辨去每个角色拥有的方法。 - 更强了代码的可扩展性
- 在处理复杂的角色关系的时候可以使得不同类型角色之间隔离开来。代码的清晰度更高,无论是开发者还是调用者
python中一切皆对象
类和类型是一个概念,基础的数据类型也是以类的方式来开发的,例如:通过对象来调用Str类中的__str__()。
创建一个类
-
从语法机制上讲,class就可以创建一个类。但是从内部机制上讲,type是所有类的元类,object是所有类的父类。
类也是被创建出来的,type创建类,type(cls) = type。 -
type(obj): 输出的结果就是这个对象所属的类
-
type(cls): 输出的结果就是创建这个类的元类,大多数情况下就是type,除非指定的metaclass=ABCMeta
from abc import ABCMeta class A(metaclass=ABCMeta): # 指定metaclass pass print(type(A)) class B: pass # 不指定metaclass print(type(B)) ''' 输出结果: <class 'abc.ABCMeta'> <class 'type'> '''
类中方法的内存地址和对象中方法的内存地址问题以及类的加载顺序问题
-
类的内存地址中存储着变量和方法
-
对象的内存地址中存储着类的内存中存储方法的内存地址的变量名的内存地址(这里涉及到指针的问题)
class Person: country = 'CHINA' print(country) # 与函数不同,类不通过实例化调用,就可以直接在加载的时候直接打印类中的静态变量 # 但是类中的方法需要等到类实例化后由实例化对象来调用,函数也需要通过函数函数名来调用 # print(Person.country) # 这里不能直接用类名调用country静态变量,因为类并未加载完成,也没有把类空间中的数据赋值给类名 ''' 类的加载顺序: 1. 类先按照从上往下一行一行代码加载,遇到可以直接输出静态变量,但是类的方法不能调用 2. 当加载完成后,系统发现是一个类,会给类创建一个类空间(类的命名空间),空间中存储着所有的静态变量和方法信息 3. 此时再把空间赋值给类名,才可以通过类名来调用 ''' def func(self): print('66666') def fun1(self): print('7777777') p1 = Person() print(p1.func) print(Person.func) ''' 结果是: ''' CHINA <bound method Person.func of <__main__.Person object at 0x10634afd0>> <function Person.func at 0x106343f80>