11 面向对象
编程思维
- 面向过程编程:一遇到问题马上想到的是怎么写代码把这个功能实现
- 函数式编程:一遇到问题马上想到的是有没有一个函数已经把这个功能实现了,如有就拿过来用,没有就定义一个有这个功能的函数
- 面向对象编程:一遇到问题马上想到的是有没有一个类中有这个方法能把这个功能实现,如没有就创建这个类
11.1 类和对象
- 什么是类、对象?
类就是拥有相同属性和相同功能的对象的集合;
对象就是类的实例
-
定义类(说清楚共同属性和功能是哪些)
语法: class 类名(): 类的说明文档 类的内容(包含属性和方法) 说明: class - 关键字 类名 - 程序员自己命名 要求:标识符,不能是关键字 规范:驼峰命名法,首字母大写;见名知意;不使用系统的函数名、类名、模块名 类的说明文档 - 用''''''引起来的说明性文字,主要说清楚类提供的属性和功能 类的内容 - 主要包含属性和方法(定义在类中的函数叫方法)
# 定义一个人类 class Human(object): '''实例化人类的类''' def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def eat(self,food): print(self.name + '正在吃' + food) def sleep(self): print(self.name + '正在睡') p1 = Human('xiaoming', 18,'man')
- 创建对象(定义对象)
语法: 类() 说明: 类名 - 已经定义好的类的类名
p1 = Human('xiaoming', 18,'man') # 创建人类的对象p1
11.2 方法
11.2.1 三种方法
类中的方法分为三种:对象方法、类方法、静态方法
-
对象方法
- 定义: 直接定义在类中的函数就是对象方法
-
特点:自带参数self(self在对象调用时不用传参,系统会自动将当前对象传给self)谁调用指向谁
- 调用:用对象调用(对象.方法名())
-
类方法
- 定义:定义函数前加装饰器:
@classmethod
- 特点:自带参数cls(cls在类调用时不用传参,系统会自动将当前类传给cls)
- 调用:用类调用(类.方法名())
- 定义:定义函数前加装饰器:
-
静态方法
- 定义:定义函数前加装饰器
@staticmethod
- 特点:没有默认参数
- 调用:用类调用(类.方法名())
- 定义:定义函数前加装饰器
-
对象方法、类方法、静态方法怎么选?
- 如果实现函数的功能需要用到对象的属性,就选用对象方法
- 如果实现函数的功能需要用到类的属性,就选用对象方法
- 如果实现函数的功能不需要用到对象的属性和类的属性,就选用静态方法
class Dog():
def eat(self):
print('狗啃骨头')
# count 就是类方法
@classmethod
def count(cls):
# Dog能做的cls都能做
dog2 = Dog()
dog3 = cls()
print(dog2, dog3) # <__main__.Dog object at 0x000001425276A860> <__main__.Dog object at 0x000001425276A550>
print('狗的数量是100')
@staticmethod
def func():
print('静态方法')
# 创建对象
dog1 = Dog()
# 对象方法调用
dog1.eat()
# 类方法调用
Dog.count()
# 静态方法调用
Dog.func()
注意
从本质上讲,类中的所有方法都可以用对象和类调用,但不建议这样做
Dog.eat(12) # 如果用类调用对象方法,self就会变成普通的参数,没有存在的价值
dog1.count() # 如果用对象调用类方法,cls指向的还是类(不会指向对象)且对象不存在还要创建对象
11.2.2 __init__方法
- 构造方法
构造方法:函数名和类名是一样的,用来创建对象的方法就是构造方法(Python中的构造方法,在定义类的时候系统已经自动创建好了)
- __init__方法
__init__方法又叫做初始化方法,用来创建对象的时候对对象进行初始化操作
当通过类创建对象的时候,系统会自动调用__init__方法来对创建出的对象进行初始化
调用构造方法创建对象的时候需不需要参数,需要几个,看被自动调用的__init__方法
class Person():
def __init__(self,a,b):
print('初始化方法',a,b)
p1 = Person(10, 20) # 初始化方法10 20
p2 = Person(a=100, b=200) # 初始化方法100 200
11.2.3 属性
- 属性:对象属性和类属性
- 对象属性:对象属性的值会因为对象不同而不一样
- 定义在__init__方法中
self.属性名 = 值
- 类属性:直接定义在类中的变量
- 类属性的值不会因为对象不同而不一样
类名.属性名 = 值
def Person():
def __init__(self, name,age, gender):
self.name = name
self.age = age
self.gender = gender
p1 = Person('张三' ,19 ,'男')
print(p1.name, p1.age, p1.gender)
# 修改属性
p1.age = 28
print(p1.age)
def __repr__(self):
# 当通过print打印当前类的对象的时候会自动调用这个函数,获取函数的返回值为打印的值(返回值必须是字符串)
return f'< name:{self.name} age:{self.age} sid:{self.sid} >'
- 对象属性的增删改查
class Student():
def __init__(self, name, age=0, gender='男', score=0):
self.name = name
self.age = age
self.gender = gender
self.score = score
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
stu1 = Student('小明')
print(stu1)
#<'name': '小明', 'age': 0, 'gender': '男', 'score': 0>
1)查(获取属性的值)
-
对象.属性
- 获取对象指定属性对应的值 -
getattr(对象,属性名[,默认值])
- 获取对象指定属性对应的值,属性不存在时,可以添加默认值
print(stu1.name)
print(stu1,'name1') # AttributeError
print(getattr(stu1,'name'))
print(getaar(stu1,'name1') ) # AttributeError
print(getattr(stu1,'name1','无名氏'))
- 改、查
-
对象,属性 = 值
- 当属性不存在就给对象添加属性,属性存在的时候就修改 -
setattr(对象,属性名, 默认值)
- 当属性不存在就给对象添加属性,属性存在的时候就用默认值修改
self.name = '小工'
print(self.name)
self.height = 180
print(self.height)
setattr(stu1,'weight',60)
setattr(stu1,'age',18)
print(stu1)
- 删
-
del 对象.属性
- 删除对象中指定的属性,属性不存在报错 -
delattr(对象,属性名)
- 删除对象中指定的属性,属性不存在报错
del stu1.weight
delattr(stu1,'gender')
print(stu1)
- 内置属性
Python在定义类的时候系统自动添加的属性(从基类中继承下来的属性)就是内置属性
class Dog():
# 类属性
num = 100
# 对象属性
def __init__(self, name, age=1, color='黄色'):
self.name = name
self.age = age
self.color = color
def __repr__(self):
c = self.__class__
return '<' + c.__module__ + '.' + c.__name__ + '类的对象' + ':' + str(self.__dict__)[1:-1] + '>'
# 方法
def eat(self, food):
print(f'{self.name}在吃{food}')
@classmethod
def show_num(cls):
print(f'狗的数量:{cls.num}')
@staticmethod
def bark():
print('狗在嗷嗷叫')
dog1 = Dog('大黄')
1) __module__
类.__module__
可以获取定义的模块的模块名(python中所有的数据类型都是类)
print(Dog.__module__) # __main__
print(int.__module__) # builtins
2)__class__
对象.__class__
可以获取对象对应的类
print(dog1.__class__) # <class '__main__.Dog'>
print(type(dog1)) # <class '__main__.Dog'>
__name__
类.__name__
可以获取类名
print(Dog.__name__) # 'Dog'
__dict__
类.__dict__
可以将类转换成字典(类的类属性名作为key,类属性的值作为值)
对象.__dict__
可以将对象转换成字典(类的类属性名作为key,类属性的值作为值)
print(Dog.__dict__)
print(dog1.__dict__)
__doc__
类.__doc__
可以获取类的说明文档
print(int.__doc__)
6)__base__
、__bases__
类.__base__
可以获取当前类的父类
类.__bases__
可以获取当前类的所有父类
print(Dog.__base__) # <class 'object'>
print(Dog.__bases__) # (<class 'object'>,)
__slots__
__slots__
可以约束当前类的对象能够拥有哪些对象属性,但如果使用了__slots__
,那么这个类的对象就不能再使用__dict__
内置属性,因为__slots__
的使用将属性的存储方式从字典类型转为元组存储
class Person():
__slots__ = ('name', 'age', 'height')
def __init__(self,name, age=10):
self.name = name
self.age = age
p1 = Person('小明')
p1.name1 = 'xiaoming'
11.3 私有化
- 访问权限(在其他语言中,访问权限如下)
- 公开的:在类的内部和外部都能使用,也可被继承
- 保护的:在类的内部可以使用,在外部不能使用们可以被继承
- 私有的:只能在内部使用,也不能被继承
严格来说,Python中所有的属性和方法都是公开的,这儿所说的私有化其实是假私有化
class Person():
num = 100
__num = 61
def __init__(self):
self.name = '小明'
self.age = 10
self.__gender = '男'
def eat(self):
print(f'{self.name}在吃饭')
p1 = Person()
print(Person.num)
print(p1.name, p1.age)
p1.eat()
# print(Person.__num) # 报错
# print(p1.__gender) # 报错
print(p1.__dict__) # {'name': '小明', 'age': 18, '_Person__gender': '男'}
print(p1._Person__gender) # 男
11.4 getter和setter
- getter和setter的作用
- getter :在获取某个属性值之前想做别的事情,就给这个属性添加getter
- setter :在给属性赋值之前想做别的事情,就给这个属性添加setter
- getter和setter的添加方法
1)添加getter
a. 在需要添加getter的属性名前加_
b. 定义一个getter对应的函数,需要@property
装饰器,函数名就是不带_
的属性名,函数需要一个返回值
c. 获取属性值时通过对象.不带_的属性名
(本质是在调用getter对应的函数,取到的属性值就是函数的返回值)
2)添加setter
如果想给属性添加setter,必须保证属性有getter
a. 添加getter
b. 定义setter对应的函数,需要@getter函数名.setter
装饰器,函数名就是不带_
的属性名,需要一个参数接收给属性赋的值,不需要返回值
c. 获取属性值时通过对象.不带_的属性名
(本质是在调用setter对应的函数)
class Rect:
def __init__(self, length=0, width=0):
self.length = length
self.width = width
self._area = length * width
@property
def area(self):
self._area = self.width * self.length
return self._area
@area.setter
def area(self,value):
#self._area = value # 一般采用这种方式重新赋值
raise ValueError # 因为本案列中面积不能自己赋值,所以用产生错误提示用户不能赋值
r1 = Rect(4, 5)
print(r1.area)
r1.width = 10
print(r1.area)
r1.length = 10
print(r1.area)
r1.area = 91 # ValueError
11.5 继承
- 什么是继承
让子类直接拥有父类的属性和方法的过程就是继承
父类(超类) - 被继承者
子类 - 继承者
- 怎么继承
class 类名(父类1,父类2,...):
类的说明文档
类的内容
注意:默认情况下,定义的类继承自object
class Person:
num = 61
def __init__(self):
print('Person中init')
self.name = '小明'
self.age = 18
self.gender = '男'
self.__sex = '女'
def eat(self):
print(f'Person:{self.name}在吃饭')
@classmethod
def show_num(cls):
print(f'人类的数量:{cls.num}')
@staticmethod
def func1():
print('人类破坏环境!')
# 让 Student(子类) 继承 Person(父类)
class Student(Person):
pass
# 继承的时候子类可以直接拥有父类所有的属性和方法
print(Student.num)
stu = Student()
print(stu.name, stu.age, stu.gender)
stu.eat()
Student.show_num()
Student.func1()
print(stu.__dict__)
- 在子类中添加内容
1)在子类中添加类属性和方法
类属性和方法的添加不会因为继承而受到任何影响
2)添加对象属性
对象属性的继承:继承的时候因为init方法被继承,间接继承了对象继承
在子类的init方法中通过 super()
去调用父类的__init__
方法
类中的方法的调用过程(多态):
通过类或者对象在调用方法的时候,会先看当前类中有没有这个方法,如果有就直接调用自己类中的方法;没有就看父类中有没有定义这个方法,如果父类定义了就调用父类的;父类没有定义,就看父类的父类中有没有…以此类推,如果objec类没有定义才会操作
class Teacher(Person):
title = '老师'
def __init__(self):
# 调用父类的__init__方法
super().__init__()
self.school = '郑华中学'
self.subject = 'python'
self.tea_id = '001'
def attend_class(self):
print('老师上课')
print(Teacher.num,Teacher.title)
t1 = Teacher()
t1.attend_class()
print(t1.school, t1.subject, t1.tea_id)
print(t1.name)
- 多继承
class Animal():
num = 10
def __init__(self,age,gender):
self.age = age
self.gender = gender
def eat(self):
print('动物需要吃东西')
class Fly():
name = '飞行器'
def __init__(self,height,time):
self.height = height
self.time = time
def stop(self):
self.height = 0
print('停止飞行')
class Bird(Animal, Fly):
pass
b1 = Bird(2,'雌')
# 两个父类的类属性都能继承
print(Bird.num, Fly.name)
# 对象属性只会继承第一个父类的
print(b1.age, b1.gender)
# print(b1.height,b1.time) # AttributeError
# 两个父类的不同方法都可以继承,同名方法只会继承一个
b1.eat()
b1.stop()
面试题:
class A:
def show(self):
print('A')
class B(A):
def show(self):
super().show()
print('B')
class C(A):
def show(self):
super().show()
print('C')
class D(B, C):
def show(self):
super().show()
print('D')
d = D()
d.show
面试题扩展:
class A:
def show(self):
print('A')
class B(A):
def show(self):
super().show()
print('B')
class C(A):
def show(self):
super().show()
print('C')
class D(A):
def show(self):
super().show()
print('D')
class E(B, C):
def show(self):
super().show()
print('E')
class F(C, D):
def show(self):
super().show()
print('F')
class G(F, E):
def show(self):
super().show()
print('G')
print('=========G========')
g = G()
g.show()
print('==================')
f = F()
f.show()
super的用法:
super 用法补充
super(类, 对象) - 获取指定类的父类(对象必须是类对象;类默认指向当前类,当前默认是当前对象)
class A:
def show(self):
print('A')
pass
class B(A):
def show(self):
print('B')
class B2:
def show(self):
print('B2')
class C(B, B2):
def show(self):
# super().show() # super(C, self).show()
# super(C, self).show()
super(B, B()).show()
print('C')
print('============C:=============')
c = C()
c.show()
11.6 运算符重载
python中使用每一个运算符其本质都是在调用运算符对应的方法(每个运算符都会对应一个固定的方法)。
某种类型的数据支不支持某个运算符,就看这个数据对应的类型中有没有实现运算符对应的方法。
如果这个类型没有实现运算符对应的方法或这个方法不满足需求,那么我们可以自己定制这个方法
10 + 20 # 10.__add__(20)
'abc' + '123' # 'abc'.__add__('123')
class Person:
def __init__(self, name='', age=0):
self.name = name
self.age = age
# +
def __add__(self, other):
# self + other
return self.age + other.age
# *
def __mul__(self, other):
list1 = []
for _ in range(other):
list1.append(copy(self))
return list1
# <
# 注意:大于符号和小于符号实现其中一个另外一个也可以用
def __lt__(self, other):
return self.age < other.age
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
p1 = Person('小明', age=18)
p2 = Person(age=30)
print(p1 == p2)
print(p1 + p2) # p1.__add__(p2)
# 10+20 -> 10.__add__(20)
# 10 * 2
print(p1 * 3) # []
persons = [Person('小花', 20), Person('Tom', 18), Person('张三', 28), Person('李四', 15)]
persons.sort()
print(persons)
# print(p1 > p2)
注意:大于符号和小于符号实现其中一个另外一个也可以用
11.7 单例模式
- 单例类
from copy import copy
class Person:
__obj = None
def __new__(cls, *args, **kwargs):
# print('new')
# new_obj = super().__new__(cls)
# print('new:', new_obj)
# return new_obj
if not cls.__obj:
cls.__obj = super().__new__(cls)
return cls.__obj
def __init__(self):
print('init:', self)
p1 = Person()
p2 = Person()
p3 = copy(p1)
print(p1 is p2)
print(p1 is p3)
"""
def Person(*args, **kwargs):
对象 = Person.__new__()
对象.__init__(*args, **kwargs)
return 对象
"""