Python基础学习13--面向对象
面向对象
面向对象概述:
类和对象的概念:
-
类 : 抽象名词,用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
-
对象 : 具体的事物,通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
-
类跟对象的关系:
-> 一个是抽象,代表一大类事物------比如:人类,动物,花,等等。。
->一个是具象,代表了一类事物中具体的的某一个个体------比如:人类中的张三、李四、王五等。。
类的内容:
-
表明事物的特征,称为属性(变量)
-
表明事物功能或动作,称为成员方法(函数)
类
类的定义:
-
必须使用class关键字------函数使用的是def
-
class后面跟 类的名称
-
类是由属性和方法构成
-
成员的属性定义可以直接使用变量赋值,如果没有值,可使用None
类的命名:
-
大驼峰:由一个或多个单词构成,每个单词的首字母大写,单词跟单词之间直接相连
-
可以通过默认内置变量检查类和对象的所有成员
1. 对象所有成员检查
# dict前后各有两个下划线
obj.__dict__
2. 类所有成员检查
class_name.__dict__
代码实例如下:
# 定以一个学生类,用来形容学生 # 定义一个空的类 class Student(): # 一个空类,pass代表直接跳过 # 此处pass必须有 pass # 实例化类 # 变量 = 类名() Tom = Student() # 在定义一个类,用来描述学习Python的学生 class PythonStudent(): # 用None给不确定的值赋值 name = None age = 18 course = "Python" # 需要注意 # 1. def doHomework的缩进层级 # 2. 系统默认有一个self参数 def doHomework(self): print("我在做作业") # 推荐在函数末尾使用return语句 return None # 实例化一个叫wilson的学生,是一个具体的人 wilson = PythonStudent() # 访问对象成员 print('wilson.name:',wilson.name) print('wilson.age :',wilson.age) # 注意成员函数的调用没有传递进入参数 wilson.doHomework() print('wilson.__dict__:',wilson.__dict__) print('PythonStudent.__dict__:',PythonStudent.__dict__) wilson.name = 'Null' wilson.age = 16 print('wilson.__dict__:',wilson.__dict__) print('PythonStudent.__dict__:',PythonStudent.__dict__)
输出结果如下:
wilson.name: None wilson.age : 18 我在做作业 wilson.__dict__: {} PythonStudent.__dict__: {'__module__': '__main__', 'name': None, 'age': 18, 'course': 'Python', 'doHomework': <function PythonStudent.doHomework at 0x02B114F8>, '__dict__': <attribute '__dict__' of 'PythonStudent' objects>, '__weakref__': <attribute '__weakref__' of 'PythonStudent' objects>, '__doc__': None} wilson.__dict__: {'name': 'Null', 'age': 16} PythonStudent.__dict__: {'__module__': '__main__', 'name': None, 'age': 18, 'course': 'Python', 'doHomework': <function PythonStudent.doHomework at 0x02B114F8>, '__dict__': <attribute '__dict__' of 'PythonStudent' objects>, '__weakref__': <attribute '__weakref__' of 'PythonStudent' objects>, '__doc__': None}
关于self
-
self在对象的方法中表示当前对象本身,如果通过对象调用一个方法,那么该对象会自动传入到当前方法 的第一个参数中
-
self并不是关键字,只是一个用于接收对象的普通参数,理论上可以用任何一个普通变量名代替
-
方法中有self形参的方法称为非绑定类的方法,可以通过对象访问, 没有self的是绑定类的方法, 只能通过类访问
-
使用类访问绑定类的方法时, 如果类方法中需要访问当前类的成员,可以通过 __class__成员名来访问
代码实例如下:
class Teacher(): name = "Jack" age = 33 def Math(self): self.name = "Tony" self.age = 31 print("My name is {0}".format(self.name)) # 调用类的成员变量需要用 __class__ print("My age is {0}".format(__class__.age)) def English(): print(__class__.name) print(__class__.age) print("Are u ok?") t = Teacher() t.Math() # 调用绑定类函数使用类名 Teacher.English()
输出结果如下:
My name is Tony My age is 33 Jack 33 Are u ok?
面向对象的三大特性
- 封装
- 集成
- 多态
封装
封装就是对对象的成员进行访问限制
封装的的3个级别:
- 公开、公共:Public
- 公共的封装实际对成员没有任何操作,任何地方都可以访问
- 受保护的:Protected
- 受保护的封装是将对象成员进行一定级别的封装,在类实例或者子类实例都可以进行访问,但是外部不可以访问
- 封装方法:在成员前加1个下划线即可
- 私有的:Private
- 私有成员是最高级别的封装,只能在当前类或对象中访问,在类的外部无法直接进行访问,包括子类
- 在成员前面添加2个下划线即可
- 私有成员是最高级别的封装,只能在当前类或对象中访问,在类的外部无法直接进行访问,包括子类
- 公开、公共:Public
代码实例如下:
class Person(): # name是共有的成员 name = "Chinese" # sex是受保护的 _sex = 'male' # __age就是私有变量 __age = 18 def Old(self): print('{} years old'.format(self.__age)) p = Person() # name是公有变量 print('p.name:',p.name) # sex是受保护的,可以在实例中调用 print('p._sex:',p._sex) # 当前类或对象中可访问 p.Old() # __age是私有变量,只能在当前的类或对象中访问,实例不能访问私有变量 print(p.__age)
输出结果如下:
p.name: Chinese p._sex: male 18 years old # 只能通过当前类或对象访问 AttributeError: 'Person' object has no attribute '__language' # 不能直接访问私有变量
继承
继承就是一个类可以获得另外一个类中的成员属性和成员方法
作用:减少代码,增加代码的复用功能,同时可以设置类与类直接的关系
继承与被继承的关系
-
- 被继承的类称为父类,也叫基类、超类
- 用于继承的类,称为子类,也叫派上类
- 继承与被继承一定存在一个 is-a 的关系
继承的特征:
-
- 所有的类都继承自object类,即所有的类都是object类的子类
- 子类一旦继承父类,则可以使用父类中除私有成员外所有的内容
- 子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
- 子类可以定义独有的成员属性和方法
- 子类中定义的成员如果和父类的成员相同,则优先使用子类成员
- 子类如果想扩充父类的方法,可以在定义新方法的同时访问父类成员来进行代码重用,可以使用 [父类名.父类成员] 的格式来调用父类成员,也可以使用 super().父类成员 的格式调用
代码实例如下:
# 子类和父类定义同一个变量名称,则优先使用子类本身 # 人有工作的函数, 老师也有工作的函数,但老师的工作需要讲课 class Person(): name = "NoName" age = 18 __score = 0 # 考试成绩是秘密,只要自己知道 _petname = "sec" # 小名,是保护的,子类可以用,但不能公用 def sleep(self): print("Sleeping ... ...") def work(self): print("show me the money") # 父类写在括号内 class Teacher(Person): teacher_id = "9527" name = "Tony" def teach(self): print("Lecture to students") def work(self): # 扩充父类的功能只需要调用父类相应的函数 # Person.work(self) # 扩充父类的另一种方法 # super代表得到父类 super().work() self.teach() t = Teacher() # 子类和父类定义同一个变量名称,则优先使用子类本身 print('t.name:',t.name) # 扩充后,会调用父类和子类的方法 t.work()
输出结果如下:
t.name: Tony # t.work() show me the money # 继承自父类Person的work() Lecture to students # 子类Teacher扩充的work()
继承变量函数的查找顺序问题:
-
- 优先查找自身的变量
- 如果没有,则查找父类
- 构造函数如果子类中没有定义,则自动调用父类的构造函数
- 如果子类有定义,则不向上查找
构造函数
-
- 是一类特殊的函数,在类进行实例化之前进行调用
- 如果定义了构造函数,则实例化的时候使用自身的构造函数,不会查找父类的构造函数
- 如果没有定义,会自动查找父类的构造函数
- 如果子类没有定义,父类的构造函数带参数,则构造对象时的参数应该按照父类参数传参
代码实例如下:
# 例1:实例会直接调用构造函数 class Animal: pass class Reptiles(Animal): # Reptiles:爬行动物 # __init__就是构造函数/构造方法 # 每次实例化的时候,第一个被自动调用 def __init__(self): print('这是Reptiles-Animal的子类') # 实例化的时候,自动调用Reptiles的构造函数 R1 = Reptiles() # 例2:如果子类中存在构造函数,则不会再向上查找 class Tortoise(Reptiles): # Tortoise:乌龟 def __init__(self,name): print('这是Tortoise-Reptiles的子类:{}'.format(name)) # 实例化的时候,自动调用Tortoise的构造函数 T1 = Tortoise('turtle') class Snake(Reptiles): pass # 实例化的时候,如果子类没有构造函数,所以会向上查找,即其父类-Reptiles S1 = Snake() class Terrapin(Tortoise): # Terrapin:水龟 pass # 此时,由于Terrapin没有构造函数,则向上查找 # 因为Tortoise的构造函数需要2个参数,实例化的时候给了1个,报错 T2 = Terrapin()
输出结果如下:
# 实例化的时候,自动调用Reptiles的构造函数 # R1 = Reptiles() 这是Reptiles-Animal的子类 # 实例化的时候,自动调用Tortoise的构造函数 # T1 = Tortoise('turtle') 这是Tortoise-Reptiles的子类:turtle # 实例化的时候,如果子类没有构造函数,所以会向上查找,即其父类-Reptiles # S1 = Snake() 这是Reptiles-Animal的子类 # 子类没有构造函数,父类的构造函数需要2个参数,如果只传1个,会报错! TypeError: __init__() missing 1 required positional argument: 'name'
单继承和多继承
-
- 单继承:每个类只能继承一个类
- 多继承:每个类允许继承多个类
单继承和多继承的优缺点
-
- 单继承:
- 优点:传承有序,逻辑清晰,语法简单,隐患少
- 缺点:功能不饿能无限扩展,只能在当前唯一的继承链中扩展
- 多继承:
- 优点:类的功能扩展方便
- 缺点:继承关系混乱
- 单继承:
代码实例如下:
# 子类可以直接拥有父类的属性和方法,私有属性和方法除外 class Fish(): def __init__(self, name): self.name = name def swim(self): print("我会游泳...") class Bird(): def __init__(self, name): self.name = name def fly(self): print("我会飞...") class Person(): def __init__(self, name): self.name = name def work(self): print("Show me the money...") # 单继承的例子 class Student(Person): def __init__(self, name): self.name = name stu = Student("stu") print(stu.name) stu.work() # 多继承的例子 class SuperMan(Person, Bird, Fish): # 继承3个父类:Person, Bird, Fish def __init__(self, name): self.name = name class SwimMan(Person, Fish): # 继承2个父类:Person, Fish def __init__(self, name): self.name = name Kent = SuperMan("Clark") print(Kent.name) Kent.fly() Kent.swim() Kent.work()
输出结果如下:
# stu继承自Person stu Show me the money... # Clark继承自Person, Bird, Fish Clark 我会飞... 我会游泳... Show me the money...
多态
-
多态就是同一个对象在不同情况下有不同的状态出现
-
多态不是语法,而是一种设计思想
-
多态性:一种调用方式,不同的执行效果
-
多态:同一事物的多种形态,如:动物分为人类,鸟类,爬行类,鱼类......
-
Mixin设计模式:Mix-in 混入的意思
-
使用Mixin实现多继承的时候非常小心
- 首先它必须表示某一单一功能,而不是某个物品
- 指责必须单一,如果由多个功能,则写多个Mixin
- Mixin不能依赖于子类的实现
- 子类即使没有继承这个Mixin类,也能照样工作,只是缺少了某个功能
-
优点
- 使用Mixin可以在不对类进行任何修改的情况下,扩充功能
- 可以方便地组织和维护不同功能组件的划分
- 可以根据需要任意调整功能类的组合
- 可以避免创建很多新的类,导致类的继承混乱
代码实例如下:
class Vehicle(object): # Vehicle:交通工具 pass class PlaneMixin(object): def fly(self): print('I am flying') # 多继承,父类为:Vehicle, PlaneMixin class Airplane(Vehicle, PlaneMixin): pass # 第二个父类为 PlaneMixin 而非 Plane # 不影响功能,但会告诉读代码的人,Airplane 只是 Vehicle,不是 Plane # 换言之,Airplane 属于 Vehicle类,但是他又具备 Plane 的 fly 功能 # Mixin,表示混入(mix-in),它告诉别人,这个类(PlaneMixin)是作为功能添加到子类中,而不是作为父类
类的相关函数
1、issubclass(A, B)
判断 A 类是否是 B 类的子类
class A(): pass class B(A): pass class C(): pass print(issubclass(B, A)) # 判断 B 是否为 A 的子类 print(issubclass(C, A)) # 判断 C 是否为 A 的子类 print(issubclass(B, object)) # 判断 B 是否为 object 的子类
# B 是 A 的子类,所以为 True True # C 不是 A 的子类,所以为 False False # 所有类都是 object 的子类,所以为True True
2、isinstance(obj, A)
判断 obj 对象是否是 A 类的实例对象
class A(): pass a = A() print(isinstance(a, A)) # 判断 a 是否是 A 的实例对象 print(isinstance(A, A)) # 判断 A 是否是 A 的实例对象
# a 是 A 的实例对象,所以为True True # A 不是 A 的实例对象,所以为False False
3、hasattr(obj, name)
判断 obj 对象是否存在成员 name
class A(): name = "NoName" a = A() print(hasattr(a, "name")) # 判断 实例对象 a 中是否包含 name 属性 print(hasattr(a, "age")) # 判断 实例对象 a 中是否包含 age 属性
# 判断 实例对象 a 中包含 name 属性 True # 判断 实例对象 a 中不包含 age 属性 False
4、getattr(object, name[, default])
返回一个对象属性值
class A(object): bar = 1 a = A() print(getattr(a, 'bar')) # 属性 bar 存在,则获取属性 bar 值 print(getattr(a, 'bar2', 3)) # 属性 bar2 不存在,但设置了默认值 3 ,会返回 3 print(getattr(a, 'bar2')) # 属性 bar2 不存在,会触发异常报错
# 属性 bar 存在,则获取属性 bar 值 1 1 # 属性 bar2 不存在,但设置了默认值 3 ,会返回 3 3 # 属性 bar2 不存在,又没有设置默认值,会触发异常报错 AttributeError: 'A' object has no attribute 'bar2'
5、setattr(object, name, value)
用于设置属性值,该属性不一定是存在的
class A(object): bar = 1 a = A() # 属性 bar 存在 print(getattr(a, 'bar')) # 获取属性 bar 值 setattr(a, 'bar', 5) # 设置属性 bar 值为 5 print(a.bar) # 查看设置后 bar 的值 # 属性 obj 不存在 setattr(a, "name", 'wilson') # 如果属性不存在,会创建一个新的属性,并对属性赋值 print(a.name) # 查看 name 属性的值
# 使用 getattr 查看 bar 的属性值 1 # 使用 setattr 更改 bar 的属性值后,再次查看 5 # 使用 setattr 创建新的属性并赋值后,查看 wilson
6、delattr(object, name)
用于删除属性
class A(object): bar = 1 a = A() # 查看属性值 print(a.bar) delattr(A, 'bar') # 通过 delattr 删除 A 类中的 bar 属性 print(a.bar)
# 执行delattr(A, 'bar')操作前,查看 a.bar 的值 1 # 执行delattr(A, 'bar')操作后,查看 a.bar 的值 AttributeError: 'A' object has no attribute 'bar'
推荐歌曲:生僻字(计算机版)(Cover:陈柯宇)