面向對象
一、內置類屬性
1. 聲明類屬性
聲明類的時候系統自動添加的屬性(可能是字段也可能是對象屬性)
class Person:
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def eat(self, food):
print('{}在吃{}'.format(self.name, food))
p1 = Person('小明', '男', 18)
1)__name__
類的字段;類名.__name__
:獲取當前類名(字符串)
print(type(Person)) # <class 'type'>
print(Person.__name__) # Person
print(type(Person.__name__)) # <class 'str'>
2)__doc__
類的字段;類名.__doc__
:獲取類的說明文檔
print(Person.__doc__)
"""
說明文檔: 人類
name - 名字
gender - 性別
age - 年齡
"""
3)__class
對象屬性;對象.__class__
:獲取對象對應的類,返回的是類(和type功能相同)
print(p1.__class__) # <class '__main__.Person'>
4)__dict__
對象屬性;對象.__dict__
:將對象中所有的屬性和對應的值轉換成一個字典中的鍵值對(一個對象一個字典)
類的字段;類.dict:將類轉換成一個字典,字典中的元素是類中所有的字段和對應的值
print(p1.__dict__) # {'name': '小明', 'gender': '男', 'age': 18}
print(Person.__dict__)
'''
{'__module__': '__main__', '__doc__': '\n\t說明文檔: 人類\n\tname - 名字\n\tgender - 性別\n\tage - 年齡\n\t', '__init__': <function Person.__init__ at 0x1007ac400>, 'eat': <function Person.eat at 0x1007ac510>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}
'''
定制當前對象的打印
a. 重寫str方法,這個方法的返回值就是對應的打印結果(類型必須是字符串)
class Person:
...
def __str__(self):
return ''.join(['<', str(self.__dict__)[1:-1], '>'])
print(p1) # <'name': '小明', 'gender': '男', 'age': 18>
b. 重寫repr方法,這個方法的返回值就是對應的打印結果(類型必須是字符串)
class Person:
...
def __repr__(self):
return ''.join(['<', str(self.__dict__)[1:-1], '>'])
print(p1) # <'name': '小明', 'gender': '男', 'age': 18>
注意
:如果設置了slots的值,當前類的對象就不能使用dict屬性
5)__module__
類的字段;類.__module__
:獲取當前類是在哪個模塊中聲明的(返回的是模塊的名字)
print(Person.__module__) # __main__
print(bool.__module__) # builtins
6)__bases__
類的字段;類.__bases__
:獲取當前類的父類(返回的是一個元組)
print(Person.__bases__) # (<class 'object'>,)
2.私有化
1)訪問權限:公開、保護、私有
公開
:公開的屬性和方法在類的內部、外部可以使用,可以被繼承
保護
:保護的屬性和方法在類的內部可以使用,外部不能使用, 可以被繼承
私有
:私有的屬性和方法只能在在類的內部使用,外部不能使用, 也不能被繼承
2)python中屬性和方法的訪問權限
python中類的所有屬性和方法本質都是公開的;
私有化是假私有化,只是提示程序員這個屬性或者方法在外部不要使用,也不要去繼承
怎么私有化:在需要私有化的屬性名或者方法名前加__
(注意
:不能以__
結尾)
python私有化的原理:在私有的屬性和方法前加了'_類名'
3.屬性的getter和setter
1)什么是getter和setter
當我們需要在獲取屬性值之前對其進行其他操作,則需要對其添加getter;
當我們需要在給屬性值之前對其進行其他操作,則需要對其添加setter
2)給屬性添加getter
a. 屬性命名時前面加下劃線'_'
b. 在@property裝飾器的后面聲明一個對象方法
第一,將屬性去掉下劃線作為方法名
第二,方法除了self外不需要其他參數
第三,函數的返回值就是獲取這個屬性時得到的值
c. 在外部使用屬性時,通過 '對象.不帶下劃線的屬性名' 去使用
注意
:獲取屬性指定值的時候, 就會自動去調用getter對應的方法
3)給屬性添加getter
屬性添加setter之前必須先添加getter
a. 保證屬性名前有下劃線
b. 在@getter名.setter裝飾器的后面聲明一個同名對象方法,需要一個self以外的參數,不需要返回值
c. 在外部使用屬性時,通過 '對象.不帶下劃線的屬性名' 去使用
注意
:當給屬性賦值時, 實質是調用setter對應的方法
class Person:
def __init__(self, name, age, gender):
self.name = name
self._age = self.age = age
self.__gender = self.gender = gender
@property
def age(self):
print('年齡被訪問!')
return self._age
@age.setter
def age(self, value):
print('年齡被修改!')
if not isinstance(value, int):
raise ValueError
if not 0 <= value <= 120:
raise ValueError
self._age = value
@property
def gender(self):
if self.__gender == 1:
return 'male'
elif self.__gender == 2:
return 'female'
else:
return 'unknown'
@gender.setter
def gender(self, value):
if value == 'male':
self.__gender = 1
elif value == 'female':
self.__gender = 2
else:
self.__gender = 0
def __repr__(self):
return 'Person<name: {}, age: {}, gender: {}>'.format(self.name, self.age, self.gender)
練習:寫一個矩形類
# 有屬性長、寬、面積、周長
# 要求從生活的角度考慮
class WriteError(Exception):
def __str__(self):
return 'Modify read-only properties!'
class Rectangle:
def __init__(self, length, width):
self._length = self.length = length
self._width = self.width = width
self._area = self.length * self.width
self._perimeter = 2 * (self.length + self.width)
@property
def length(self):
return self._length
@length.setter
def length(self, value):
if not (isinstance(value, int) or isinstance(value, float)) or value <= 0:
raise ValueError
self._length = value
@property
def width(self):
return self._width
@width.setter
def width(self, value):
if not (isinstance(value, int) or isinstance(value, float)) or value <= 0:
raise ValueError
self._width = value
@property
def area(self):
self._area = self.length * self.width
return self._area
@area.setter
def area(self, value):
raise WriteError
@property
def perimeter(self):
self._perimeter = 2 * (self.length + self.width)
return self._perimeter
@perimeter.setter
def perimeter(self, value):
raise WriteError
def __repr__(self):
return '<length:{}, width: {}, area: {}, perimeter: {}>'. \
format(self._length, self._width, self.area, self.perimeter)
r1 = Rectangle(10, 20)
print(r1.area, r1.perimeter) # 200 60
print(r1) # <length:10, width: 20, area: 200, perimeter: 60>
r1.length = 20
print(r1.area) # 400
print(r1) # <length:20, width: 20, area: 400, perimeter: 60>
# r1.area = 100 # __main__.WriteError: Modify read-only properties!
二、類方法和靜態方法
1. 類中的函數
類中的方法分為:對象方法、類方法和靜態方法
對象方法 | 類方法 | 靜態方法 | |
---|---|---|---|
如何聲明 | 直接聲明 | 聲明在@classmethod后面 | 聲明在@staticmethod后面 |
如何調用 | 對象.方法名調用 | 類名.方法名調用 | 類名.方法名調用 |
特點 | 有指向當前對象的self | 有指向當前類的自帶參數cls,這個參數在調用時不需要手動傳參 | 沒有默認參數,是聲明在類中的一個普通方法 |
使用情景 | 如果實現的功能需要用到對象屬性,就使用對象方法 | 如果實現的功能需要用到類(不需要對象屬性),就使用類方法 | 如果實現的功能既不需要用到對象屬性,也不需要類,就使用靜態方法 |
class Person:
num = 61
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def ear(self, food):
# 對象能做的事情self都能做
print('{}在吃{}'.format(self.name, food))
@classmethod
def func1(cls):
# 類能做的事情cls都能做
# a = cls('a', 18, 'b')
# print(a)
print('這是一個類方法!')
@staticmethod
def func2():
print('這是一個靜態方法!')
def __repr__(self):
return 'Person<name: {}, age: {}, gender: {}>' \
.format(self.name, self.age, self.gender)
Person.func1()
Person.func2()
三、繼承
1. 什么是繼承
繼承就是讓子類直接擁有父類的屬性和方法
子類:繼承者
父類/超類:被繼承者
2. 如何繼承
1)語法
class 類名(父類1, 父類2, 父類3...):
說明文檔
類的內容
2)說明:
():固定寫法,如果省略相當于(object),聲明類的時候如果沒有寫父類,默認繼承object(object又稱基類)
父類:一個類的父類可以有多個,但是一般情況下只有一個父類(支持多繼承)
class Person:
num = 61
def __init__(self):
self.name = '小明'
self.age = 18
self.gender = '男'
self.__a = 10
def eat(self, food='米飯'):
print('{}在吃{}'.format(self.name, food))
@classmethod
def show(cls):
print('人類的數量:{}'.format(cls.num))
@staticmethod
def func1():
print('人類')
def func2(self):
print('我是{}'.format(self.name))
class Student(Person):
num = 10
def __init__(self):
# 在子類中添加對象屬性,需要先通過super().__init__()來繼承父類的對象屬性
super().__init__()
self.study_id = '001'
self.class1 = 'py1904'
# 重寫
@staticmethod
def func1():
print('學生')
def func2(self):
print('我是學生', end=', ')
# 在子類中可以通過super().方法()可以調用父類的方法
# 注意:super()只能在對象方法和類方法中使用
super().func2()
# 添加功能
def study(self):
print('{}在學習'.format(self.name))
stu1 = Student()
print(stu1.num) # 61
print(stu1.name, stu1.age, stu1.gender) # 小明 18 男
stu1.eat() # 小明在吃米飯
Student.show() # 人類的數量:61
print(stu1.__dict__) # {'name': '小明', 'age': 18, 'gender': '男', '_Person__a': 10, 'study_id': '001', 'class1': 'py1904'}
stu1.func1() # 學生
stu1.func2() # 我是學生, 我是小明
3. 多繼承
python中的類支持多繼承(讓一個類同時繼承多個類)
多繼承的時候子類只能繼承第一個父類所有的屬性和方法
后面的父類中只有字段和方法可以被繼承
class Animal(object):
num = 100
def __init__(self):
self.age = 0
self.gender = 'female'
@classmethod
def func1(cls):
print('動物類的類方法')
def func2(self):
print('動物類的對象方法')
class Fly(object):
name = 'fly'
def __init__(self):
self.height = 100
self.time = 5
self.speed = 100
def func2(self):
print('飛行的對象方法')
class Bird(Animal, Fly):
pass
bird = Bird()
# 字段都能繼承
print(Bird.num, Bird.name) # 100 fly
Bird.func1() # 動物類的類方法
bird.func2() # 飛行的對象方法
print(bird.age, bird.gender)
print(bird.speed, bird.height, bird.time) # AttributeError: 'Bird' object has no attribute 'speed'