2019-08-09_Note_Day15

面向對象

一、內置類屬性

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'
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容