Python-Day06-面向对象进阶

Python-100Day-学习打卡
Author: Seven_0507
Date: 2019-05-21

        今天主要总结了Python面向对象编程的进阶知识点,主要从属性、类中的方法、运算符重载、类之间的关系、继承和多态方面归纳总结。
在这里插入图片描述

Python面向对象进阶

1. 类属性与实例属性

  • 什么是类对象,实例对象

  • 类对象:类名

  • 实例对象:类创建的对象

1.1 类属性

# 类属性
class people:
    name = "Tom"    # 公有的类属性
    __age = 18      # 私有的类属性

p = people()
print(p.name)       # 实例对象
print(people.name)  # 类对象
 
print(p.__age)      #错误 不能在类外通过实例对象访问私有的类属性
print(people.__age) #错误 不能在类外通过类对象访问私有的类属性
Tom
Tom

---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-2-3efede2e003a> in <module>
      8 print(people.name)  # 类对象
      9 
---> 10 print(p.__age)      #错误 不能在类外通过实例对象访问私有的类属性
     11 print(people.__age) #错误 不能在类外通过类对象访问私有的类属性


AttributeError: 'people' object has no attribute '__age'

1.2 实例属性

# 实例属性
class people:
    name = "tom" # 公有的类属性

p = people()
p.age = 18      # 实例属性
 
print(p.name)
print(p.age)    # 实例属性是实例对象特有的,类对象不能拥有
 
print(people.name) # 类属性,可以调用
print(people.age)  # 错误:实例属性,不能通过类对象调用
tom
18
tom

---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-3-0c71d7231365> in <module>
     10 
     11 print(people.name) # 类属性,可以调用
---> 12 print(people.age)  # 错误:实例属性,不能通过类对象调用


AttributeError: type object 'people' has no attribute 'age'

1.3 类属性和实例属性混合

# 类属性和实例属性混合
class people:
    name="tom"                # 类属性:实例对象和类对象可以同时调用
    def  __init__(self,age):  # 实例属性
        self.age=age
 
p=people(18)     # 实例对象
p.sex="男"       # 实例属性
 
print(p.name)   # 类属性实例对象可以调用
print(p.age)    # 实例属性是实例对象特有的,类对象不能拥有
print(p.sex)    # 实例属性是实例对象特有的,类对象不能拥有
 
print(people.name)   # 类对象
print(people.age)  # 错误:实例属性,不能通过类对象调用
print(people.sex)  # 错误:实例属性,不能通过类对象调用
tom
18
男
tom

---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-4-3ef77090faf8> in <module>
     13 
     14 print(people.name)   # 类对象
---> 15 print(people.age)  # 错误:实例属性,不能通过类对象调用
     16 print(people.sex)  # 错误:实例属性,不能通过类对象调用


AttributeError: type object 'people' has no attribute 'age'

1.4 修改类属性和实例属性的区别

"""
如果在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,
会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且如果通过
实例对象引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性
"""
class Animal:
    name = "Panda"    # 类属性

print(Animal.name)  # 类对象引用类属性
p = Animal()
print(p.name)       # 实例对象引用类属性时,会产生一个同名的实例属性
p.name = "dog"      # 修改的只是实例属性的,不会影响到类属性
print(p.name)       # dog
print(Animal.name)  # panda
# 删除实例属性
del p.name
print(p.name)
Panda
Panda
dog
Panda
Panda

2. @property包装器

"""
--(1) 使用原有有方法定义访问器/修改器/删除器
"""

class Car(object):

    def __init__(self, brand, max_speed):
        self.set_brand(brand)
        self.set_max_speed(max_speed)

    def get_brand(self):
        return self._brand

    def set_brand(self, brand):
        self._brand = brand

    def get_max_speed(self):
        return self._max_speed

    def set_max_speed(self, max_speed):
        if max_speed < 0:
            raise ValueError('Invalid max speed for car')
        self._max_speed = max_speed

    def __str__(self):
        return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)

    # 用已有的修改器和访问器定义属性
    brand = property(get_brand, set_brand)
    max_speed = property(get_max_speed, set_max_speed)


car = Car('QQ', 120)
print(car)
# car.max_speed = -100   # 检查赋给属性的值是否正确,不符合输入要求会报错
car.max_speed = 320
car.brand = "Benz"
print(car)
print(Car.brand)
print(Car.brand.fget)
print(Car.brand.fset)
Car: [品牌=QQ, 最高时速=120]
Car: [品牌=Benz, 最高时速=320]
<property object at 0x03F58A50>
<function Car.get_brand at 0x03F4F468>
<function Car.set_brand at 0x03F4F3D8>
  • 属性访问器-getter
  • 属性修改器-setter
  • 属性删除器-deleter
"""
--(2)访问器/修改器/删除器——包装器
"""

class Car(object):

    __slots__ = ('_brand', '_max_speed') # 限定自定义类型的对象只能绑定brand/max_speed属性

    def __init__(self, brand, max_speed):
        self._brand = brand
        self._max_speed = max_speed

    # 访问器 - getter方法(把一个方法变成属性)
    @property
    def brand(self):
        return self._brand

    # 修改器 - setter方法(属性赋值)
    @brand.setter
    def brand(self, brand):
        self._brand = brand

    # 删除器 - deleter方法(删除属性)
    @brand.deleter
    def brand(self):
        del self._brand

    @property
    def max_speed(self):
        return self._max_speed

    @max_speed.setter
    def max_speed(self, max_speed):
        if max_speed < 0:
            raise ValueError('Invalid max speed for car')
        self._max_speed = max_speed

    def __str__(self):
        return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)


car = Car('QQ', 120)
print(car)
# car.max_speed = -100   # 检查赋给属性的值是否正确,不符合输入要求会报错
car.max_speed = 320
car.brand = "Benz"
# car.current_speed = 80  # 会报错,使用__slots__属性限制只能绑定brand/max_speed属性
print(car)
# del car.max_speed  # 会报错,未提供删除器
del car.brand 
# 属性的实现
print(Car.brand)
print(Car.brand.fget)
print(Car.brand.fset)
print(Car.brand.fdel)
Car: [品牌=QQ, 最高时速=120]
Car: [品牌=Benz, 最高时速=320]
<property object at 0x03F5FFC0>
<function Car.brand at 0x03F4F6A8>
<function Car.brand at 0x03F4F660>
<function Car.brand at 0x03F4F858>

3. __slots__魔法

class Person(object):

    # 限定Person对象只能绑定_name, _age和_gender属性
    __slots__ = ('_name', '_age', '_gender')

    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在玩飞行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 22)
    person.play()
    person._gender = '男'
    person.phone='13000000' # 此句报错,由于person定义时phone没有被放入__slots__中

if __name__ == '__main__':
    main()
王大锤正在玩斗地主.

---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-38-f938be77850d> in <module>
     34 
     35 if __name__ == '__main__':
---> 36     main()


<ipython-input-38-f938be77850d> in main()
     31     person.play()
     32     person._gender = '男'
---> 33     person.phone='13000000' # 此句报错,由于person定义时phone没有被放入__slots__中
     34 
     35 if __name__ == '__main__':


AttributeError: 'Person' object has no attribute 'phone'

4. 类中的方法

4.1 实例方法

若类中定义的方法是发送给对象的消息,则是实例方法,之前实例中我们在类中定义的方法都是对象方法。

4.2 类方法

# (1) 类方法的定义与调用

class people:
    country = "china"

    @classmethod
    def getCountry(cls):
        return cls.country
p = people()
print(p.getCountry())       # 实例对象调用类方法
print(people.getCountry())  # 类对象调用类方法
china
china
# (2) 通过类方法修改类属性

class people:
    country="china"

    @classmethod
    def getCountry(cls):
        return cls.country
    @classmethod
    def setCountry(cls,country):
        cls.country = country

# 类方法的调用
p = people()
print(p.getCountry())       # 实例对象调用类方法
print(people.getCountry())  # 类对象调用类方法

# 类方法修改类属性
p.setCountry("Japan")
print(p.getCountry())
print(people.getCountry())
china
china
Japan
Japan
# (3) 类方法的应用

from time import time, localtime, sleep


class Clock(object):
    """数字时钟"""

    def __init__(self, hour=0, minute=0, second=0):
        self._hour = hour
        self._minute = minute
        self._second = second

    @classmethod # now为类方法
    def now(cls):
        ctime = localtime(time())
        return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)

    def run(self):
        """走字"""
        self._second += 1
        if self._second == 60:
            self._second = 0
            self._minute += 1
            if self._minute == 60:
                self._minute = 0
                self._hour += 1
                if self._hour == 24:
                    self._hour = 0

    def show(self):
        """显示时间"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)


def main():
    # 通过类方法创建对象并获取系统时间
    clock = Clock.now()
    count = 0
    while True:
        print(clock.show())
        sleep(1)
        clock.run()
        count += 1
        if count == 6:
            break


if __name__ == '__main__':
    main()
18:39:27
18:39:28
18:39:29
18:39:30
18:39:31
18:39:32

4.3 静态方法

# (1) 静态方法定义与调用

class people3:
    country = "china"

    @staticmethod
    def getCountry():
        return people3.country # 在静态方法中引用类属性的话,必须通过类对象来引用

p = people3()
print(p.getCountry())        # 实例对象调用静态方法
print(people3.getCountry())  # 类对象调用静态方法
china
china
# 静态方法的应用

from math import sqrt
"""
我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长
未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不
是对象方法,这个方法是属于三角形类而并不属于三角形对象的
"""

class Triangle(object):

    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

    # 静态方法
    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

    # 实例方法
    def perimeter(self):
        return self._a + self._b + self._c

    # 实例方法
    def area(self):
        half = self.perimeter() / 2
        return sqrt(half * (half - self._a) * (half - self._b) * (half - self._c))


def main():
    a, b, c = 3, 4, 1 
    # 静态方法和类方法都是通过给类发消息来调用的
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
        # print(Triangle.perimeter(t))
        print(t.area())
        # print(Triangle.area(t))
    else:
        print('无法构成三角形.')


if __name__ == '__main__':
    main()
无法构成三角形.

4. 运算符重载

"""
运算符重载:__gt__ / __lt__
"""

class Student(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    def study(self, course_name):
        print('%s正在学习%s.' % (self._name, course_name))

    def watch_tv(self):
        if self._age < 18:
            print('%s只能观看《熊出没》.' % self._name)
        else:
            print('%s正在观看《死神来了》.' % self._name)

    # 重载大于(>)运算符
    def __gt__(self, other):
        return self._age > other._age

    # 重载小于(<)运算符
    def __lt__(self, other):
        return self._age < other._age


if __name__ == '__main__':
    stu1 = Student('五月猴', 27)
    stu1.study('Python程序设计')
    stu1.watch_tv()
    stu2 = Student('王二小', 15)
    stu2.study('思想品德')
    stu2.watch_tv()
    print(stu1 > stu2)
    print(stu1 < stu2)
五月猴正在学习Python程序设计.
五月猴正在观看《死神来了》.
王二小正在学习思想品德.
王二小只能观看《熊出没》.
True
False
"""
运算符重载 - 自定义分数类
"""

from math import gcd


class Rational(object):

    def __init__(self, num, den=1):
        if den == 0:
            raise ValueError('分母不能为0')
        self._num = num
        self._den = den
        self.normalize()

    def simplify(self):
        x = abs(self._num)
        y = abs(self._den)
        factor = gcd(x, y)
        if factor > 1:
            self._num //= factor
            self._den //= factor
        return self

    def normalize(self):
        if self._den < 0:
            self._den = -self._den
            self._num = -self._num
        return self

    def __add__(self, other):
        new_num = self._num * other._den + other._num * self._den
        new_den = self._den * other._den
        return Rational(new_num, new_den).simplify().normalize()

    def __sub__(self, other):
        new_num = self._num * other._den - other._num * self._den
        new_den = self._den * other._den
        return Rational(new_num, new_den).simplify().normalize()

    def __mul__(self, other):
        new_num = self._num * other._num
        new_den = self._den * other._den
        return Rational(new_num, new_den).simplify().normalize()

    def __truediv__(self, other):
        new_num = self._num * other._den
        new_den = self._den * other._num
        return Rational(new_num, new_den).simplify().normalize()

    def __str__(self):
        if self._num == 0:
            return '0'
        elif self._den == 1:
            return str(self._num)
        else:
            return '(%d/%d)' % (self._num, self._den)


if __name__ == '__main__':
    r1 = Rational(2, 3)
    print(r1)
    r2 = Rational(6, -8)
    print(r2)
    print(r2.simplify())
    print('%s + %s = %s' % (r1, r2, r1 + r2))
    print('%s - %s = %s' % (r1, r2, r1 - r2))
    print('%s * %s = %s' % (r1, r2, r1 * r2))
    print('%s / %s = %s' % (r1, r2, r1 / r2))
(2/3)
(-6/8)
(-3/4)
(2/3) + (-3/4) = (-1/12)
(2/3) - (-3/4) = (17/12)
(2/3) * (-3/4) = (-1/2)
(2/3) / (-3/4) = (-8/9)

5. 类之间的关系

5.1 关联关系

"""
对象之间的关联关系
"""
from math import sqrt


class Point(object):

    def __init__(self, x=0, y=0):
        self._x = x
        self._y = y

    def move_to(self, x, y):
        self._x = x
        self._y = y

    def move_by(self, dx, dy):
        self._x += dx
        self._y += dy

    def distance_to(self, other):
        dx = self._x - other._x
        dy = self._y - other._y
        return sqrt(dx ** 2 + dy ** 2)

    def __str__(self):
        return '(%s, %s)' % (str(self._x), str(self._y))


class Line(object):

    def __init__(self, start=Point(0, 0), end=Point(0, 0)):
        self._start = start
        self._end = end

    @property
    def start(self):
        return self._start

    @start.setter
    def start(self, start):
        self._start = start

    @property
    def end(self):
        return self.end

    @end.setter
    def end(self, end):
        self._end = end

    @property
    def length(self):
        return self._start.distance_to(self._end)


if __name__ == '__main__':
    p1 = Point(3, 5)
    print(p1)
    p2 = Point(-2, -1.5)
    print(p2)
    line = Line(p1, p2)
    print(line.length)
    line.start.move_to(2, 1)
    line.end = Point(1, 2)
    print(line.length)
(3, 5)
(-2, -1.5)
8.200609733428363
1.4142135623730951

5.2 依赖关系

"""
对象之间的依赖关
"""
class Car(object):

    def __init__(self, brand, max_speed):
        self._brand = brand
        self._max_speed = max_speed
        self._current_speed = 0

    @property
    def brand(self):
        return self._brand

    def accelerate(self, delta):
        self._current_speed += delta
        if self._current_speed > self._max_speed:
            self._current_speed = self._max_speed

    def brake(self):
        self._current_speed = 0

    def __str__(self):
        return '%s当前时速%d' % (self._brand, self._current_speed)


class Student(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    # 学生和车之间存在依赖关系 - 学生使用了汽车
    def drive(self, car):
        print('%s驾驶着%s欢快的行驶在去西天的路上' % (self._name, car._brand))
        car.accelerate(30)
        print(car)
        car.accelerate(50)
        print(car)
        car.accelerate(50)
        print(car)


if __name__ == '__main__':
    stu = Student('王小二', 15)
    car = Car('QQ', 120)
    stu.drive(car)
王小二驾驶着QQ欢快的行驶在去西天的路上
QQ当前时速30
QQ当前时速80
QQ当前时速120

6. 继承

"""
(1) 继承示例
-继承的语法
-调用父类方法
-子类中定义特有的属性和方法
"""

class Person(object):
    """人(Person父类)"""

    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        print('%s正在愉快的玩耍.' % self._name)

    def watch_tv(self):
        if self._age >= 18:
            print('%s正在观看死神来了.' % self._name)
        else:
            print('%s只能观看《熊出没》.' % self._name)


class Student(Person):
    """学生(子类继承Person父类)"""

    def __init__(self, name, age, grade):
        super().__init__(name, age) 
        self._grade = grade  # 定义自己特有的grade属性

    @property
    # 定义自己特有的grade方法
    def grade(self):
        return self._grade

    @grade.setter
    def grade(self, grade):
        self._grade = grade
    
    # 定义自己特有的study方法 
    def study(self, course):
        print('%s的%s正在学习%s.' % (self._grade, self._name, course))


class Teacher(Person):
    """老师(子类继承Person父类)"""

    def __init__(self, name, age, title):
        super().__init__(name, age) 
        self._title = title  # 定义自己特有的title属性

    @property
    # 定义自己特有的title方法
    def title(self):
        return self._title

    @title.setter
    def title(self, title):
        self._title = title

    # 定义自己特有的teach方法
    def teach(self, course):
        print('%s%s正在讲%s.' % (self._name, self._title, course))


def main():
    stu = Student('王二小', 15, '初三')
    stu.study('数学')  # 调用子类方法
    stu.watch_tv()    # 调用父类方法
    t = Teacher('五月猴', 27, '研究僧')
    t.teach('Python程序设计')  # 调用子类方法
    t.watch_tv()              # 调用父类方法


if __name__ == '__main__':
    main()
初三的王二小正在学习数学.
王二小只能观看《熊出没》.
五月猴研究僧正在讲Python程序设计.
五月猴正在观看死神来了.
"""
(2) 多重继承
- 菱形继承(钻石继承)
- C3算法(替代DFS的算法)
"""

class A(object):

    def foo(self):
        print('foo of A')


class B(A):
    """B(子类继承A父类)"""
    pass


class C(A):
    """C(子类继承A父类)"""

    def foo(self):
        print('foo fo C')


class D(B, C):
    """D(子类继承B,C父类)"""
    pass


class E(D):
    """E(子类继承D父类)"""

    def foo(self):
        print('foo in E')
        super().foo()  # 继承父类方法
        super(B, self).foo()  # 继承B父类方法
        super(C, self).foo()  # 继承C父类方法


if __name__ == '__main__':
    d = D()
    d.foo()
    print('-'*50)
    e = E()
    e.foo()
foo fo C
--------------------------------------------------
foo in E
foo fo C
foo fo C
foo of A
"""
(3) 多重继承应用
- 通过多重继承可以给一个类的对象具备多方面的能力
- 这样在设计类的时候可以避免设计太多层次的复杂的继承关系
"""


class Father(object):

    def __init__(self, name):
        self._name = name

    def gamble(self):
        print('%s在打麻将.' % self._name)

    def eat(self):
        print('%s在大吃大喝.' % self._name)


class Monk(object):

    def __init__(self, name):
        self._name = name

    def eat(self):
        print('%s在吃斋.' % self._name)

    def chant(self):
        print('%s在念经.' % self._name)


class Musician(object):

    def __init__(self, name):
        self._name = name

    def eat(self):
        print('%s在细嚼慢咽.' % self._name)

    def play_piano(self):
        print('%s在弹钢琴.' % self._name)


# 试一试下面的代码看看有什么区别
# class Son(Monk, Father, Musician):
# class Son(Musician, Father, Monk):


class Son(Musician, Father, Monk):

    def __init__(self, name):
        Father.__init__(self, name)
        Monk.__init__(self, name)
        Musician.__init__(self, name)


son = Son('王大锤')
son.gamble()
# 调用继承自Father的eat方法
son.eat()
son.chant()
son.play_piano()
王大锤在打麻将.
王大锤在细嚼慢咽.
王大锤在念经.
王大锤在弹钢琴.

7. 多态

# 多态
from abc import ABCMeta, abstractmethod


class Pet(object, metaclass=ABCMeta):
    """
    宠物(Pet父类)
    Pet被创建为一个抽象类,就是不能够创建对象的类,这种类是专门为让其他类去继承它
    通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果
    """

    def __init__(self, nickname):
        self._nickname = nickname

    @abstractmethod
    def make_voice(self):
        """发出声音(Pet父类定义原始方法)"""
        pass


class Dog(Pet):
    """狗(Dog子类继承Pet父类)"""

    def make_voice(self):
        # 对父类中make_voice方法进行重写
        print('%s: 汪汪汪...' % self._nickname)


class Cat(Pet):
    """猫(Cat子类继承Pet父类)"""
    
    def make_voice(self):
        # 对父类中make_voice方法进行重写
        print('%s: 喵...喵...' % self._nickname)


def main():
    pets = [Dog('旺财'), Cat('凯蒂')]
    for pet in pets:
        pet.make_voice()


if __name__ == '__main__':
    main()
旺财: 汪汪汪...
凯蒂: 喵...喵...

猜你喜欢

转载自blog.csdn.net/KEEP_GIONG/article/details/90417796