目录
今日总结
一、对象属性的增删改查
1.查
获取属性的值
- 对象.属性 - 获取执行属性对应的值,如果属性不存在报错
- getattr(对象, 属性名) - 获取执行属性对应的值,如果属性不存在报错
- getattr(对象, 属性名, 默认值) - 获取执行属性对应的值,如果属性不存在不报错,直接返回指定的默认值
class Student:
def __init__(self, name, gender='男', age=18, tel=''):
self.name = name
self.gender = gender
self.age = age
self.tel = tel
def __repr__(self):
return str(self.__dict__)
stu1 = Student('小明', '男', 18, '110')
print(stu1.name)
print(getattr(stu1, 'name'))
# 方法二可以动态确定需要获取的内容
value = input('学生的哪个信息: name? age? tel? gender?:')
print(stu1.value)
print(getattr(stu1, value))
# print(stu1.score) # AttributeError: 'Student' object has no attribute 'score'
print(getattr(stu1, 'score', 0))
2. 增、改
- 对象.属性 = 值 - 如果属性存在就是修改,属性不存在就是添加
- setattr(对象, 属性名, 值) - 如果属性存在就是修改,属性不存在就是添加
stu1.name = 'bobo'
print(stu1)
stu1.score = 100
print(stu1, stu1.score)
setattr(stu1, 'height', 180)
print(stu1)
setattr(stu1, 'age', 23)
print(stu1)
3. 删
- del 对象.属性
- delattr(对象, 属性名)
del stu1.gender
print(stu1)
# print(stu1.gender) # 报错!AttributeError: 'Student' object has no attribute 'gender'
delattr(stu1, 'tel')
print(stu1)
二、内置属性
1. _dict_
- 类._dict_ - 类装换成字典,将所有的类行书名作为键,类属性对应的值作为值
- 对象._dict_ - 将对象转换成字典,对象属性名是键,对象属相对应的值作为值
class A:
x = 10
def __init__(self, m=10,n='hello',p=True):
self.m = m
self.n = n
self.p = p
self.name = '小红'
def f1(self):
print(f'对象方法:{self.m}')
@classmethod
def f2(cls):
print(f'类方法:{cls.x}')
@staticmethod
def f3():
print(f'静态方法')
a = A()
print(A.__dict__)
print(a.__dict__) # {'m': 10, 'n': 'hello', 'p': True}
2. _class_
- 对象._class_ - 获取对象对应的类,功能和type一样
print(a.__class__, type(a))
3. _name_
- 类._name_ - 获取类名
print(A.__name__) # 'A'
4. _doc_
- 类._doc_ - 获取类的说明文档
print(int.__doc__)
5. _module_
- 类._module_ - 获取定义指定类的模块名
print(A.__module__, int.__module__)
6._base_
- 类._base_ - 获取当前类的父类
- 类._bases_ - 获取当前类的所有父类
print(A.__base__) # <class 'object'>
print(A.__bases__) # (<class 'object'>,)
class Student:
# 类属性__slots__是用来约束当前类的对象最多有那些对象属性
# 注意:如果给类的__sloat__属性赋值了,那么这个类的对象就不能再使用__dict__属性
__slots__ = ('name')
def __init__(self):
self.name = '小明'
pass
stu = Student()
print(stu.name)
# stu = Student()
# print(stu.age) # 报错!AttributeError: 'Student' object has no attribute 'age'
stu.nme = '小蓝'
三、私有化
1. 访问权限(面向对象语言通用)
公开的:属性和方法可以在类的内部、类的外部都可以使用,也可以被继承(python中所有的属性和方法都是公开的)
保护的:属相和方法在类的内部使用,也可以被继承
私有的:属相和方法只能在类的内部使用,不能被继承
2. python私有化是假的:在名字前加__
class A:
num = 18
__x = 20
def __init__(self):
self.__name = '小明'
def f1(self):
print(A.num)
@classmethod
def f2(cls):
print('内部:', A.__x)
A.__f3()
@staticmethod
def __f3():
pass
# print(A.num)
A.f2()
# print('外部:', A.__x)
# print(A.__dict__)
print('外部:', A._A__x)
a = A()
# print(a.__name)
# A.__f3()
四、类的继承
1.什么是继承
继承就是让子类直接拥有父类的属性和方法
子类 - 继承者
父类 - 被继承者,又叫超类
2. 继承的语法
class 类名(父类1,父类2,父类3,...):
类的内容
注意:如果在定义类的时候没有添加继承关系,那么这个类默认继承Python的基类object
class Person:
num = 61
def __init__(self):
self.name = 'bobo'
self.age = 18
self.gender = '男'
def eat(self, food):
print(f'{self.name}在吃{food}')
class Student(Person):
pass
print(Student.num) # 61
stu1 = Student()
print(stu1.name, stu1.age, stu1.gender)
stu1.eat('香菜')
3. 在子类中添加属性和方法
- 添加类属性和方法
在子类中直接定义新的类属性和新的方法
- 添加对象属性
类中的方法的调用过程:先看当前类中是否有对应的方法,如果有就直接调用自己类中的方法,如果没有就看父类中有没有。如果有调用父类的方法,如果没有再看父类的父类......以此类推,如找到object都没有找到相应的方法,才会报错
继承的时候,子类之所以可以继承父类的对象属性,是因为继承了父类的__init__方法。如果子类中有__init__,那么父类的__init__就不会被继承,那么对象属性就不会被继承
总结:在子类中添加__init__的同时用super()去调用父类的__init__方法
class Teacher(Person):
profession = '老师'
def __init__(self):
# super().__init__() # 调用当前类的父类的__init__()
super().__init__()
self.school = '清华'
def teach(self):
print('教学')
@staticmethod
def message():
print('教书育人')
rint(Teacher.num, Teacher.profession) # 61 老师
t1 = Teacher()
t1.eat('火锅') # bobo在吃火锅
t1.teach()
Teacher.message() # 教书育人
print(t1.school)
print(t1.name, t1.age, t1.gender) # bobo 18 男
4. super的使用
- super(类,对象).方法 - 调用指定类的父类的方法(注意:后面的对象必须是前面的)
- super().方法 - 调用当前类的父类的方法
class A:
def funcA(self):
print('A')
def func1(self):
print('A1')
class B(A):
def funcB(self):
print('B')
def func1(self):
print('B1')
class C(B):
def funcC(self):
print('C')
def func1(self):
print('C1')
# super().func1()
# super(C, self).func1()
# super(B, self).func1()
super(Student, Student()).eat('苹果')
print('==============================================')
c = C()
c.func1()
class Animal:
def __init__(self, age, gender='雄'):
self.gender = gender
self.age = age
class Dog(Animal):
def __init__(self, name, color, gender='雄'):
super().__init__(0, gender)
self.name = name
self.color = color
a = Animal(2)
d = Dog('大黄', '黄色')
# 思考题:多继承的时候是不是所有父类的所有属性和方法都能被继承?
五、运算符重载
1. 魔法方法
类中自带的以__开头并且以两个__结尾的方法就是魔法方法,所有的魔法方法都不需要程序员去调用,会在特定的情况下自动调用
这些魔法方法中有一部分方法是在对象使用运算符的时候会被自动调用的
每一个运算的都有一个固定的魔法方法,某种数据是否支持某种,就看这个数据对应的类中有没有实现对应的魔法方法
10 + 20 # 10.__add__(20)
'abc' + '234' # 'abc'.__add__('234')
10.2 > 20
# {'a': 10} + {'b': 20} # TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
# class A:
# def func1(self):
# print('A')
#
#
# class B:
# def func1(self):
# print('B')
#
# x = B()
# x.func1()
2. 重载运算符
在自己的类中通过实现运算符的魔法方法来让自己的类的对象支持指定的运算符
from copy import copy
class Student:
def __init__(self, name='小明', age=18, score=60):
self.name = name
self.age = age
self.score = score
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
# self + other, 返回值就是加法运算的结果
def __add__(self, other):
# self = stu1, other=stu2
# return self.age + other.age
return [self, other]
def __mul__(self, other: int):
result = []
for _ in range(other):
result.append(copy(self))
return result
# self > other
def __gt__(self, other):
return self.score > other.score
stu1 = Student()
stu2 = Student('小明', 22, 45)
print(stu1 != stu2)
print(stu1 + stu2) # stu1.__add__(stu2)
s = stu1 * 3
print(s)
stu1.name = 'bobo'
print(s)
students = [
Student('stu1', 23, 98),
Student('stu2', 18, 87),
Student('stu3', 25, 99),
Student('stu4', 22, 80)
]
print(students)
print(max(students))
students.sort()
print(students)
六、 拷贝
不管是浅拷贝还是深拷贝都是将被拷贝的数据复制产生一个新的数据,然后将新的数据的地址返回。如果被拷贝的对象中有子对象(可变数据),浅拷贝不会对子对象进行拷贝(人变成不一样的人,但是狗还是同一条狗),深拷贝会对子对象进行拷贝(人变成不一样的人,狗也会变成不一样的狗)
class Dog:
def __init__(self, name, color):
self.name = name
self.color = color
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}, id:{id(self)}>'
class Person:
def __init__(self, name, age, dog=None):
self.name = name
self.age = age
self.dog = dog
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}, id:{id(self)}>'
p1 = Person('小明', 18, Dog('大黄', '黄色'))
print('p1:', p1)
# 直接赋值 - 两个变量保存同一个数据的地址
p2 = p1
print('p2:', p2)
# 浅拷贝
p3 = copy(p1)
print('p3:', p3)
# 深拷贝
p4 = deepcopy(p1)
print('p4:', p4)
print('======================修改年龄后==================')
p1.age = 20
p1.dog.name = '旺财'
print('p1:', p1)
print('p2:', p2)
print('p3:', p3)
print('p4:', p4)
七、内存管理
1. 内存的申请
当使用变量保存数据的时候系统会自动在堆中申请空间保存数据,同时让变量保存数据在内存中地址(python中的变量全都是指针数据)
如果需要保存的数据是不可变的数据,系统会先检查当前内存中是否已经存这个数据,如果有直接返回已经保存的数据的地址,如果没有才会重新申请新的地址
a = 100
b = 100
print(id(a), id(b)) # 4428060448 4428060448
x = 'abc'
y = 'abc'
print(id(x), id(y)) # 4445987760 4445987760
m = (10, 20)
n = (10, 20)
print(id(m), id(n)) # 4468933696 4468933696
l1 = [10, 20]
l2 = [10, 20]
print(id(l1), id(l2)) # 4511662208 4511664384
2. 内存释放
原则:如果数据有引用数据就不会被销毁,如果数据没有引用(引用计数为0)就会被自动销毁(释放)
list1 = [10, 20, 30]
list2 = list1
list3 = [1, list1]