Day17、18、19-面向对象
面向对象1
编程思想
-
面向过程编程 - 逻辑
一遇到问题马上想到的是怎么用算法或者步骤(逻辑)直接解决问题
-
函数式编程 - 函数
一遇到问题马上想到有没有函数具有解决这个问题的功能,如果有直接用,没有就造一个
-
面向对象编程 - 类和对象
一遇到问题马上想到有没有一个对象具有解决这个问题的功能,如果有直接用,没有就创建一个
类和对象
类就是拥有相同功能和相同属性的对象的集合 - 抽象的概念
对象就是类的实例 - 具体的表现
-
定义类 - 用代码描述清楚这个类是拥有哪些相同功能和哪些相同属性的对象集合
""" 1)、语法 class 类名: 说明文档 类的内容 2)说明 class - 关键字;固定写法 类名 - 程序员自己命名 要求:是标识符 规范:驼峰式命名并且首字母大写;见名知义;不使用系统函数名、类名、模块名 类的说明文档 - 本质就是使用三个双引号开头和结尾的注释 类的内容 - 主要包括方法(对象方法、类方法和静态方法)和属性(对象属性、类属性) 方法:本质就是定义在类中的函数,是用来对类的功能进行描述 属性:本质就是在类中保存变量,是用来对类的属性进行描述 """
-
定义对象
类名.()
对象方法
方法就是定义在类中的函数
什么是对象方法
怎么定义:直接定义在类中函数
怎么调用:通过对象.方法名
的形式来调用
特点:自带self
,用对象调用的时候self
不需要传参,系统会自动将当前对象传给self
构造方法和init
方法
- 构造函数(方法)
函数名和类名,专门用来创建对象的函数就是构造函数(方法)。
python在定义类的时候,系统会自动创建这个类对应的构造函数
python中调用构造函数创建对象的时候内部会完成3个操作:
- 调用
__new__
方法创建对象 - 用创建好的对象调用
__init__
方法,对对象进行初始化 - 返回初始化完成后的对象
-
__init__
方法魔法方法:类中方法名用
__
开头的__
结尾的方法就叫魔法方法,魔法方法不需要程序员主动调用,系统会在特定情况下自动去调用。__init__
- 每次通过类创建对象的时候,系统都会自动调用这个方法
- 创建对象的时候构造函数有没有参数有几个看对应的
init
方法除了self
以外有没有额外的参数 - 如果需要给类添加
init
方法主要保证方法名是__init__
,以及方法的性质是对象方法
属性
属性本质就是用来保存数据的变量
"""
类中的属性分为:类属性和对象属性
1)类属性
类属性 - 类属性又叫类的字段
怎么定义:直接定义子在类里面函数外面的变量就是类属性
怎么使用:通过'类.属性'的形式来使用
什么时候用:属性值不会因为对象不同而不一样,这样的属性就可以定义成类属性
2)对象属性
怎么定义:以'self.属性名=值'的形式定义在__init__方法中
怎么使用:
什么时候用:属性值会因为对象不同而不一样,这样的属性就可以定义成对象属性
"""
# # 练习: 定义一个点类,属性:x坐标、y坐标,方法:以 'x:?,y:?'的形式打印点的信息
# # 要求:创建点对象的时候可以给坐标赋值,如果不赋值x坐标和y坐标都是0
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def message_point(self):
print(f'x:{self.x},y:{self.y}')
def distance(self, point):
dis = ((self.x - point.x)**2+(self.y - point.y)**2)**0.5
return dis
point1 = Point(30, 40)
point1.message_point()
point2 = Point()
point2.message_point()
print(point2.distance(point1))
面向对象2
对象属性的增删改查
class Student:
def __init__(self, name, tel, study_id='0001', score=0):
self.name = name
self.tel = tel
self.study_id = study_id
self.score = score
# 在打印一个对象的时候,系统会自动调用这个对象去调用__repr__方法,并获取这个方法的返回值。
# 返回值是什么就打印什么(返回值必须是字符串)
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
stu1 = Student('小明', '1231313', score=90)
stu2 = Student('小红', '1123233', '0002', 78)
print(stu1)
# 1.查 - 获取对象属性的值
"""
对象.属性
getattr(对象, 属性名) - 可动态获取属性;
"""
print(stu1.name)
print(getattr(stu1, 'name'))
# value = input('请输入要获取的属性的属性名:')
# print(getattr(stu1, f'{value}'))
# print(getattr(stu1, 'height')) # 报错
# print(getattr(stu1, 'height', 180)) # 180
# 2.增/改
"""
对象.属性 = 值
setattr(对象, 属性名, 值)
"""
stu1.name = '小兰'
setattr(stu1, 'tel', '110')
print(stu1)
stu1.height = '180'
setattr(stu1, 'weight', '130')
print(stu1)
# 3.删
"""
del 对象.属性
delattr(对象, 属性名)
"""
del stu1.study_id
print(stu1)
delattr(stu1, 'score')
print(stu1)
类方法和静态方法
# 1.方法
"""
1)对象方法
怎么定义:直接定义
怎么调用:对象.方法名()
特点:self
什么时候用:如果实现函数的功能需要用到对象属性,那么这个函数就定义成对象方法
2)类方法
怎么定义:在定义函数前加装饰器@classmethod
怎么调用:类.方法名()
特点:有默认参数cls,这个参数在调用的时候不用传参,系统会自动将当前类传给cls
什么时候用:实现函数的功能在不需要对象属性的前提下需要类就使用类方法
3)静态方法
怎么定义:在定义函数前面加装饰器@staticmethod
怎么调用:类.方法名()
特点:没有默认参数(相当于类中的普通函数)
什么时候用:实现函数的功能在不需要对象属性的前提下不需要类就使用静态方法
"""
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
# 对象方法
def func1(self):
pass
@classmethod
def func2(cls):
# cls=Student
print('类方法')
# 当前类能做的cls都可以做
# stu1 = Student()
# stu2 = cls()
# print(stu1, stu2)
@staticmethod
def func3():
print('静态方法')
Student.func2()
Student.func3()
内置类属性
"""
__author__:赖良
"""
class Person:
"""
人类
"""
num = 61
def __init__(self, name='张三', age=18, gender='男'):
self.name = name
self.age = age
self.gender = gender
def eat(self, food='面条'):
print(f'{self.name}在吃{food}')
@classmethod
def message(cls):
print(f'人类目前的数量是:{cls.num}')
@staticmethod
def destroy():
print('人类破坏环境!')
p1 = Person()
# 1.__doc__ - 获取类的说明文档
print(Person.__doc__)
# print(str.__doc__)
# 2.类.__module__ - 获取指定类所在的模块
print(Person.__module__)
print(max.__module__)
# 3.对象.__class__ - 获取指定对象对应的类型,和type(对象)功能一样
print(p1.__class__)
# 4.类.__name__ - 获取类名
print(Person.__name__, type(Person.__name__))
# datas = ['abc', -0.213, '你好', 34, 90]
# for i in datas:
# with open(f'files\\{i.__class__.__name__}.txt', 'a', encoding='utf-8') as f:
# f.write(str(i)+'\n')
# 5.
# 类.__dict__ - 将类转换成字典,key是字段名,值是字段对应的值
# 对象.__dict__ - 将对象转换成字典,对象属性名作为key,属性值作为value
# print(Person.__dict__)
print(p1.__dict__)
# 6.
# 类.__base__ - 获取指定类的父类
# 类.__bases__ - 获取指定类的所有父类
# object是python中所有类的基类
print(Person.__base__)
getter和setter
# 1.getter和setter
"""
getter:
a.什么时候用:在获取对象属性前,如果要做别的什么事情,就可以给这个属性添加getter
b.怎么用:
第一步:在需要添加getter的属性的属性名前加_
第二步:在装饰器@property后面定义一个函数:
函数名就是属性名去掉_
函数没有参数,需要一个返回值(返回值就是获取这个属性真正得到的结果)
第三步:通过对象获取属性的时候,属性不需要带_
2)setter - 添加setter之前必须先添加getter
a.什么时候用:如果要在给某个对象属性赋值之前做别的什么事,就给这个属性添加setter
b.怎么用:
第一步:在需要在需要添加setter的属性名前加_
第二步:在装饰器@getter名.setter后面定义一个函数:
函数名就是属性名去掉_
函数有一个参数(这个参数指向的是赋值的时候赋的值),没有返回值
通过对象给属性赋值的时候,属性不带_
"""
class Circle:
pi = 3.1415926
def __init__(self, radius=10):
self.radius = radius
self._area = 0
@property
def area(self):
return Circle.pi * self.radius ** 2
p1 = Circle()
print(p1.radius)
print(p1.area)
p1.radius = 100
print(p1.area) # 本质是用p1去调用birth的方法,获取这个方法的返回值
# 练习:给Person添加属性age,要求:age中保存年龄值,获取age属性的时候得到的是
# 这个年龄值对应的阶段状态:儿童(0-4)、少年(5-12)、青年(13-28)
# 、壮年(29-40)、中年(41-55)、老年(55以上)
class Person:
def __init__(self):
self._age = 45
@property
def age(self):
if 0 <= self._age <= 4:
return '儿童'
elif 5 <= self._age <= 12:
return '少年'
elif 13 <= self._age <= 28:
return '青年'
elif 29 <= self._age <= 40:
return '壮年'
elif 41 <= self._age <= 55:
return '中年'
elif self._age > 55:
return '老年'
@age.setter
def age(self, value):
if type(value) != int:
raise ValueError
if value < 0 or value > 150:
raise ValueError
self._age = value
p2 = Person()
print(p2.age)
p2.age = 20
print(p2.age)
私有化
# 1.访问权限
"""
公开的:公开的属性和方法在类的内部、外部都可以使用,并且可以被继承
保护的:保护的属性和方法在类的内部可以用,外部不能用,但是可以继承
私有的:私有的属性和方法在类的内部可以用,外部不能用,不能被继承
python中的属性和方法只有一种访问权限:公开的,所谓的私有化其实是假的
私有化的方法:在属性名和方法名前加__(注意:只能是两个__开头,不能再用__结尾)
"""
class Person:
__num = 100
bart = Person()
print()
运算符重载
print(20 + 30)
print([1, 2, 3] + [10, 20])
"""
1.python在使用运算符的时候本质是在调用运算符对应的方法,每个运算符对应的方法的方法名是固定的;
不同类型的数据在参数相同的运算符的时候,会调用不同类中对应方法。
2.某个类型的数据是否支持某种运算,就看这个数据对应的类型中有没有实现这个运算符对应
"""
class Person:
def __init__(self, name='小明', age=18, gender='男'):
self.name = name
self.age = age
self.gender = gender
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
def __lt__(self, other):
return self.age < other.age
# 练习:将ps中的元素按照年龄的大小从小到大排序
p1 = Person()
p2 = Person('小花', 28, '女')
p3 = Person('张三', 20, '男')
p4 = Person('老王', 25, '男')
ps = [p1, p2, p3, p4]
print(ps)
print(sorted(ps))
print(sorted(ps, key=lambda item: item.age))
继承内存管理和拷贝
继承1
# 1.什么是继承
"""
继承是让子类直接拥有父类的属性和方法的过程就是继承
子类 - 继承者
父类 - 被继承者(又叫超类)
"""
# 2.怎么继承
"""
class 类名(父类1,父类2,父类3,...):
类的说明文档
类的内容
说明:
1) 默认情况下,类继承自object类:
class 类: == class 类名(object):
2)python中的类支持多继承
"""
class Person:
num = 61
def __init__(self, name='张三', age=18):
self.name = name
self.age = age
self.__id = '0001'
def eat(self, food='馒头'):
print(f'{self.name}吃{food}')
@staticmethod
def func1():
print('静态')
class Student(Person):
pass
# 3.继承可以让子类直接拥有父类所有的属性和方法
stu1 = Student()
print(stu1.num)
stu1.eat('肉包子')
Student.func1()
print(stu1.__dict__)
继承2
# 1.怎么在子类中添加内容
"""
1)添加类属性和方法
直接在子类中定义
2)添加对象属性
需要在子类的__init__方法中通过super()去调用父类的__init__方法
注意:静态方法不能用super
"""
class Animal:
num = 100
def __init__(self):
self.age = 0
self.gender = '雄'
def eat(self, food='艹'):
print(f'吃{food}')
class Cat(Animal):
# 添加字段
x = 'abc'
num = 10
# 添加方法
@staticmethod
def play():
print('玩球')
def eat(self, food='鱼'):
super().eat('饼干面')
print(f'吃{food}')
def __init__(self):
# super().父类的方法(静态方法中不能使用super)
super().__init__()
self.name = '花花'
cat = Cat()
print(cat.num, cat.x)
cat.play()
cat.eat()
print(cat.name, cat.age)
# 练习:定义一个教师类在Person类的基础上添加:职称和工龄两个属性,
# 并且要求创建教师对象的时候必须给名字、职称和性别赋值,年龄和工龄可以赋值可以不赋值
class Person:
def __init__(self, name, age=0, gender='男'):
self.name = name
self.age = age
self.gender = gender
class Teacher(Person):
def __init__(self, name, pro, gender, age=18, years=10):
super().__init__(name, age, gender)
self.profession = pro
self.working_years = years
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
teacher1 = Teacher('小李', '高级教师', '男')
print(teacher1.__dict__)
print(teacher1)
内存管理和拷贝
"""
定义变量保存数据的时候系统会自动在内存中开辟空间保存数据(空间的大小根据数据大小确定)。
默认情况下,使用的数据如果之前已经保存过,还是会重新申请新的内存保存这个数据,但是数字和字符串例外
一个数据是否需要释放就看这个数据引用计数是否为0,不为0就不会销毁
"""