day18-面向對象進階
1.對象屬性的增删改查
(1) 查 - 擷取屬性值
- 對象.屬性 - 擷取對象指定屬性的值,如果屬性不存在會報錯
- getattr(對象,屬性名) - 擷取對象指定屬性的值,如果屬性不存在會報錯
- getattr(對象,屬性名,預設值) - 擷取對象指定屬性的值,如果屬性不存在傳回預設值
print(stu1.name)
print(getattr(stu1, 'name'))
print(getattr(stu1, 'name', '無名氏'))
(2) 增、改
- 對象.屬性 = 值 - 當屬性存在的時候修改屬性的值,當屬性不存在的時候添加屬性
- getattr(對象,屬性名,值) - 當屬性存在的時候修改屬性的值,不存在的時候添加屬性
stu1.name = '張三'
print(stu1) # <{'name': '張三', 'age': 18, 'study_id': '000'}>
stu1.gender = '女'
print(stu1) # <{'name': '張三', 'age': 18, 'study_id': '000', 'gender': '女'}>
print(stu1.gender) # 女
setattr(stu1, 'age', 30)
print(stu1.age) # 30
setattr(stu1, 'score', 100)
print(stu1) # <{'name': '張三', 'age': 30, 'study_id': '000', 'gender': '女', 'score': 100}>
print(stu1.score) # 100
(3) 删
- del 對象.屬性 - 删除指定對象的指定屬性
- delattr(對象,屬性名) - 删除指定對象的指定屬性
del stu1.age
print(stu1) # <{'name': '張三', 'study_id': '000', 'gender': '女', 'score': 100}>
delattr(stu1, 'study_id')
print(stu1)
class A:
# __slots__屬性的值就是目前類的對象最多能擁有的對象屬性,如果為空,那麼這個類的對象就不能有對象屬性
# 注意: 如果給類設定了__slots__,那麼這個類的對象就不能再使用__dict__屬性
__slots__ = ('x', 'y', 'z')
def __init__(self):
self.x = 10
a = A()
a.y = 100
a.z = 200
# a.m = 300
2.内置屬性
class A:
"""還是計算機是"""
pass
(1) doc - 類的說明文檔
print(int.__doc__)
print(int(10).bit_length())
(2) module - 擷取類所在的子產品
print(int.__module__)
print(A.__module__)
(3) class - 擷取對象的類型,功能和type()
a =A()
print(a.__class__)
print(type(a))
(4)
- dict - 擷取類所有類屬性和對應的值,以字典的形式傳回
- dict - 擷取對象所有的對象屬性和對應的值,以字典的形式傳回(對象屬性)
(5) name - 擷取類的名字(類屬性)
- 是 ? 資料的類型
(6)
- base - 擷取目前類的父類
- bases - 擷取目前類的父類們
print(A.__base__) # <class 'object'> - 基類
print(A.__bases__) # <class 'object'>
3.運算符重載
from copy import copy
print(10 + 29)
print('abc' + '34')
print([10, 34] + [239, 0, 'abc'])
print(10 - 9)
print({23, 89} - {23})
# 10 + 29 == 10.__add__(29)
# 'abc' + '34' == 'abc'.__add__('34')
(1) python 中的運算符
- python 中每個運算符都對應一個固定的魔法方法,哪個類型中實作對應的魔法方法,哪個類型的資料就支援對應的運算符
- python 中某種資料是否支援某種運算符就看這個類中是否定義了運算符對應的魔法方法
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
# self + other
def __add__(self, other):
return self.age + other.age
# self * other
def __mul__(self, other):
return [copy(self) for _ in range(other)]
# self > other
def __gt__(self, other):
return self.age > other.age
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}, id: {id(self)}>'
# return str(self.__dict__)
stu1 = Student('小明', 18)
stu2 = Student('小花', 20)
a = stu1 + stu2 # a = stu1.__add__(stu2)
print(a)
print(stu1 * 4)
# print(stu1 in stu2)
stu_list = [stu1, stu2, Student('張三', 12)]
print(stu_list)
# print(max(stu_list, key=lambda item: item.age))
print(max(stu_list))
4.繼承
(1) 繼承
- 繼承就是讓子類直接擁有父類的屬性和方法
- 子類 - 繼承者
- 父類 - 被繼承者,又叫超類
(2) 繼承的用法
-
class 類名(父類):
說明文檔
類的内容
-
class 類名(父類1,父類2,…):
說明文檔
類的内容
- 注意:如果定義類的時候沒有寫繼承關系,那麼這個類預設繼承基類 object
- class 類名: == class 類名(object)
(3) 添加新屬性和方法
- 添加方法和添加類屬性:直接在子類中定義新的類屬性和方法
- 添加對象屬性
class Person:
num = 61
def __init__(self,):
self.name = '張三'
self.age = 30
self.gender = '男'
def eat(self, food):
print(f'{self.name}在吃{food}')
@staticmethod
def func1():
print('靜态方法')
class Student(Person):
x = '學生'
def __init__(self):
# 調用目前類的父類的__init__方法
super().__init__()
self.study_id = '0001'
self.subject = 'python'
def study(self):
print('好好學習,天天向上!')
@classmethod
def func2(cls):
print('學生的類方法')
stu = Student()
print(stu.name, stu.age, stu.gender)
stu.eat('包子')
print(Student.num)
Student.func1()
print(Student.x)
stu.study()
Student.func2()
print(stu.study_id, stu.subject)
class A:
def __init__(self, x, y):
self.x = x
self.y = y
class B(A):
def __init__(self, m, n, x=10, y=20):
super().__init__(x, y)
self.m = m
self.n = n
a = A(200, 300)
b = B(1, 2)
print(b.x, b.y)
# b.x = 3
# b.y = 4
5.繼承細節問題
(1) 子類和父類有相同方法(重寫)
class A:
def func1(self):
print('A的func1')
class B(A):
def func1(self):
print('B的func1')
B().func1()
A().func1()
(2) super 的用法
- super(類,對象).方法() - 調用指定類的父類的指定方法
- 注意:() 中的對象必須是 () 裡面類的對象
class A:
def func1(self):
print('A的func1')
class B(A):
def func2(self):
super(B, self).func1()
print('B的func2')
B().func2()
class A:
def func1(self):
print('A的func1')
def func2(self):
print('A的func2')
class B(A):
def func1(self):
print('B的func1')
class C:
def func1(self):
print('C的func1')
class D(C):
def func1(self):
super(B, B()).func2()
print('D的func1')
d = D()
d.func1()
(3) 多繼承:子類隻能繼承第一個父類的對象屬性(方法和類屬性都可以繼承)
class AA:
num = 100
def __init__(self):
self.x = 100
self.y = 200
def func1(self):
print('對象方法AA')
class BB:
message = '你好'
def __init__(self):
self.m = 100
self.n = 200
def func2(self):
print('對象方法BB')
class CC(AA, BB):
pass
c = CC()
print(CC.num, CC.message)
c.func1()
c.func2()
print(c.x, c.y)
# print(c.m, c.n) # AttributeError: 'CC' object has no attribute 'm'
(4) 私有化
- 通路權限(屬性和方法的權限):公開的、保護的、私有的
- 公開的 - 在類的外部可以使用、類的内部可以使用、也可以被繼承
- 保護的 - 在類的外部不可以使用、類的内部可以使用、也可以被繼承
- 私有的 - 隻有類的内部可以使用、不能被繼承、也不能在外部使用
- python 中類的内容的權限隻有一種:公開的
- python 的私有化:想讓屬性和方法變成私有的隻需要在名字前面加__(但是不能同時用__結尾)
- python 私有化的本質:就存儲資料的時候在私有化名字前加 ‘_類名’
- python 中的保護:在名字前加_
class A:
m = 100
__n = 200
@staticmethod
def func1():
print(A.m, A.__n)
A.__func2()
@staticmethod
def __func2():
print('私有方法')
print(A.m)
A.func1()
# print(A.__n)
# A.__func2()
print(A._A__n)
- python 之錘
6.拷貝
from copy import copy, deepcopy
(1) 拷貝
- 直接指派:直接将變量中的位址指派給另外一個變量,指派後兩個變量指向同一塊記憶體區域,并且互相影響
-
淺拷貝:清單切片、清單.copy()、字典、copy()等都是淺拷貝、copy()
: 複制原資料産生一個新的資料,降薪資料的位址傳回;如果原資料中有子對象(有可變資料),不會複制子對象
-
深拷貝:deepcopy
:複制原資料産生一個新的資料,将新的資料的位址傳回,如果原資料中有子對象,子對象也會被複制
class Dog:
def __init__(self, name, gender='公'):
self.name = name
self.gender = gender
def __repr__(self):
return str(self.__dict__)
class Person:
def __init__(self, name, age=18, dog=None):
self.name = name
self.age = age
self.dog = dog
def __repr__(self):
return str(self.__dict__)
p1 = Person('小明', dog=Dog('财财'))
p2 = p1
p3 = copy(p1)
p4 = deepcopy(p1)
print(f'p1:{p1}, id:{id(p1)}')
print(f'p2:{p2}, id:{id(p2)}')
print(f'p3:{p3}, id:{id(p3)}')
print(f'p4:{p4}, id:{id(p4)}')
print('-------------------------------------------------')
p1.name = '小花'
p1.dog.name = '大黃'
print(f'p1:{p1}, id:{id(p1)}')
print(f'p2:{p2}, id:{id(p2)}')
print(f'p3:{p3}, id:{id(p3)}')
print(f'p4:{p4}, id:{id(p4)}')
(2) 記憶體管理
- 記憶體的申請:定義變量儲存資料的時候系統會自動申請;如果定義變量儲存的時候是可變資料;每次都會申請新的内容,如果是不可變的資料,會檢查這個資料
- 釋放:如果一個資料的引用計數(引用的個數)為0,那麼這個資料就會被自動釋放
- 引用:儲存資料位址的對象就是這個資料的引用