python之面向对象

一、基本概念

  • 面向对象 vs 面向过程:
    (1)面向过程:根据业务按顺序依次编写代码,遇到需要重用的代码,则封装成函数或方法。
    (2)面向对象:是模块化思想,是对事物的进一步抽象。每个对象都是一个确定的类型,并拥有该类型的所有特征和行为。面向对象有三大特征:封装、继承和多态。

  • 类 vs 对象:
    (1)类:类是对一类事物的抽象描述。
    (2)对象:是指某一个类下面的具体事物,也称为实例。
    例如:学生是一个类,而学生小明、小红等就是学生这个类下的实例。

在python中,类和对象没有太明显的界限。所有的类、方法都被当作对象。
  • 面向对象的程序设计流程:
    (1)确定一个事物的类别,分析类别的特征和行为,封装成类。
    (2)如果业务中有多个事物,就需要分析事物之间的共性,来确定继承关系。
    (3)若是不同事物之间存在依赖,还需要设计类之间的通信方式。

二、类的封装

封装是对事物的抽象,表现形式上是将事物定义成类。类包含了该事物的特征和行为,即:每个类都包含了该类的属性和方法。

(一)属性

属性是事物特征的抽象描述。例如:学生,她的姓名、性别、学号等都是它的属性。属性是类的成员,不能独立存在。python中类的属性有类属性、实例属性、动态属性等。

  • 类属性:是指可以使用类名和实例名同时访问的属性,不同实例对象共享同一个对象地址,一改动类中的属性则所有实例中的属性信息都会更改(一改全改),如下所示:
# -*- coding: UTF-8 -*-

class Chicken:
    colour = "白色"
    num = "num1"

chicken = Chicken()
print("通过类名访问属性 colour:", Chicken.colour)
print("通过类名访问属性 num:", Chicken.num)
print()
print("通过实例访问属性 colour:", chicken.colour)
print("通过实例访问属性 num:", chicken.num)
  • 实例属性:是指只能通过实例名访问的属性。需要使用初始化函数来为不同的实例生成不同的属性,且修改某个实例属性不会影响别的实例对象的属性,如下所示:
 # -*- coding: UTF-8 -*-

class Chicken:
    colour = "白色"
    num = "num1"

    def __init__(self, weight, price):
        self.weight = weight
        self.price = price
# 创建两个实例对象
chicken1 = Chicken("2.5kg", "¥80") 
chicken2 = Chicken("3kg", "¥100")

print("chicken1 weight 地址:", id(chicken1.weight))
print("chicken1 price 地址:", id(chicken1.price))
print()
print("chicken2 colour 地址:", id(chicken2.weight))
print("chicken2 price 地址:", id(chicken2.price))
print()
# 修改实例对象的属性
chicken1.weight = "3.5kg"
chicken1.price = "¥120"
chicken1.weight = "2.0kg"
chicken1.price = "¥60"

print("实例 chicken1 访问修改后的属性 weight:", chicken1.weight)
print("实例 chicken1 访问修改后的属性 price:", chicken1.price)
print()
print("实例 chicken2 访问修改后的属性 weight:", chicken2.weight)
print("实例 chicken2 访问修改后的属性 price:", chicken2.price)
  • 动态属性:是指通过”类名.属性名“或”实例名.属性名“创建的属性,如下所示:
# -*- coding: UTF-8 -*-

class Chicken:
    colour = "白色"
    num = "num1"

Chicken.price = "¥150" # 生成了类的一个price属性
print("访问类上的动态属性:", Chicken.price)
chicken1 = Chicken()
print("类上的动态属性会传递给实例:", chicken1.price)
chicken2 = Chicken()
print("chicken1 和 chicken2 共享类上的动态属性 :", chicken1.price == chicken2.price)
print()
chicken1.weight = "5kg"
chicken2.weight = "5kg"
print("chicken1 和 chicken2 实例上的动态属性是相互独立的:", chicken1.price != chicken2.weight)
  • 特性:是一个函数,其表现形式就是一个简单的属性。访问使用特性修饰或创建的属性时,会自动调用相应的代码。python使用@property和property类来创建特性。
  1. 扩展属性功能,例如:在给属性赋值时,检查属性值是否正确。如下所示:
# -*- coding: UTF-8 -*-
class Chicken:
    def __init__(self):
        self.weight = None

    def get_weight(self):
        return self.weight

    def set_weight(self, val):
        if not isinstance(val, float):# 用于判断val的数据类型是否为float
            raise ValueError("请传递一个整数或浮点数!") # raise语句执行以后,后面的语句将不会执行。
        if 0.1 > val or val > 12:
            raise ValueError("请确保参数范围在0.1到12之间!")

        self.weight = val


chicken = Chicken()
try:
    chicken.set_weight("abc")
except Exception as e:
    print("传递字符串,触发异常:", e) # 发生异常时,将异常值显示输出

try:
    chicken.set_weight(20)
except Exception as e:
    print("传递数据超出范围,触发异常:", e)

chicken.set_weight(8.5)
print("获取正常的weight:", chicken.get_weight())
  1. @property装饰器
    python使用@property和property类来创建特性,自动调用get和set方法,如下所示:
# -*- coding: UTF-8 -*-

class Chicken:
    def __init__(self):
        self._weight = None

    @property #表示该方法使用getter特性,用于获取属性值。 
    def weight(self):
        return self._weight

    @weight.setter # 表示该方法使用setter特性,用于给属性赋值。 
    def weight(self, val):
        if not isinstance(val, float):
            raise ValueError("请传递一个整数或浮点数!")
        if 0.1 > val or val > 12:
            raise ValueError("请确保参数范围在0.1到12之间!")

        self._weight = val


chicken = Chicken()
chicken.weight = 80.5 # 将此参数范围在0.1到12之间,则可正常运行。
print("获取正常的weight:", chicken.weight)
  1. property类
    使用property类的构造函数可以封装类中的方法,并返回一个对象作为类的属性,使用过程比@property更为简洁,如下所示:
# -*- coding: UTF-8 -*-


class Chicken:
    def __init__(self):
        self.weight = "3.0kg"

    def get_weight(self):
        return self.weight

    def set_weight(self, val):
        self.weight = val

    def del_weight(self):
        del self.weight

    p_weight = property(get_weight, set_weight, del_weight) # 注意此处的参数顺序,不能修改,否则会报错。


chicken = Chicken()
print("获取weight值:", chicken.p_weight)
chicken.p_weight = "5.0kg"
print("设置weight值:", chicken.p_weight)
del chicken.weight
try:
    print("获取删除weight属性后的值:", chicken.weight)
except Exception as e:
    print("访问删除后的weight属性,触发异常:", e)

(二)方法

  1. 方法 vs 函数
  • 函数:面向过程写的代码块称为函数。
  • 方法:通过面向对象的设计,将函数写到类中,这个函数就称为这个类的方法。又进一步可划分为:实例方法、静态方法、类方法、抽象方法、动态方法。
  1. 实例方法:通过实例名称访问的方法,如下所示:
# -*- coding: UTF-8 -*-
class Chicken:

    def __init__(self, num):
        self._num = num

    def fly(self):
        print("小鸡 {0} 飞起来".format(self._num))

    def run(self):
        print("小鸡 {0} 跑起来".format(self._num))

chicken1 = Chicken("Num1")
chicken1.fly() # 实例方法
chicken1.run() # 实例方法
print()
chicken2 = Chicken("Num2")
chicken2.fly() # 实例方法
chicken2.run() # 实例方法
  1. 类方法: 使用@classmethod修饰的方法,使用过程中类名和实例名都可以访问,且使用的是同一个方法对象(与类属性的原理很类似),如下所示:
# -*- coding: UTF-8 -*-

class Chicken:
    colour = "黄色"
    count = 0

    def __init__(self, colour):
        Chicken.count = Chicken.count + 1

    @classmethod
    def get_colour(cls):
        print("cls:", cls)
        print("访问类属性,count值为:{0}".format(cls.count))


Chicken.get_colour()
print()
chicken1 = Chicken("Num2")
chicken1.get_colour()
print()
chicken2 = Chicken("Num3")
chicken2.get_colour()
print()
print("类的get_colour方法:", id(Chicken.get_colour))
print("实例 chicken1 get_colour方法:", id(chicken1.get_colour))
print("实例 chicken2 get_colour方法:", id(chicken2.get_colour))
  1. 抽象方法:使用@abstractmethod修饰的方法。对于可以实例化的对象,抽象方法的调用方式和实例方法的一样,如下所示:
from abc import ABCMeta, abstractmethod

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

    @abstractmethod
    def fly(self):
        print("小鸡 {0} 飞起来了".format(self._name))


chicken1 = Chicken("Num1")
chicken1.fly()

chicken2 = Chicken("Num2")
chicken2.fly()

对于抽象类来说,若扔使用实例方法的调用方式,程序会报错,如下所示,为解决此问题,需要使用类的继承机制。

from abc import ABCMeta, abstractmethod

class Chicken(metaclass=ABCMeta):
    def __init__(self, name):
        self._name = name

    @abstractmethod
    def fly(self):
        print("小鸡 {0} 飞起来了".format(self._name))


chicken1 = Chicken("Num1")
chicken1.fly()

chicken2 = Chicken("Num2")
chicken2.fly()
  1. 静态方法:使用@staticmethod修饰的方法,没有必需的参数。在使用上可以直接使用”类名.方法名“和”实例名.方法名“访问,并且类和不同实例的静态方法指向了同一个地址,是全局共享的。如下所示:
# -*- coding: UTF-8 -*-


class Chicken:
    weight = 0.5

    def __init__(self, num):
        self._num = num

    @staticmethod
    def get_weight():
        print("小鸡重量 {0} kg:".format(Chicken.weight))

    @staticmethod
    def run(num):
        print("小鸡 {0} 跑起来:".format(num))


Chicken.get_weight()
Chicken.run("Num1")
print()
chicken1 = Chicken("Num2")
chicken1.get_weight()
chicken1.run("Num2")
print()
chicken2 = Chicken("Num3")
chicken2.get_weight()
chicken2.run("Num3")
print()
print("类的run方法:", id(Chicken.run))
print("实例 chicken1 run方法:", id(chicken1.run))
print("实例 chicken2 run方法:", id(chicken2.run))
  1. 动态方法:给一个类动态的添加一个方法,其实现原理与动态属性一致。即:先给类添加一个属性,然后将一个定义好的方法赋值给该属性,如下所示:
# -*- coding: UTF-8 -*-

class Chicken:
    weight = 0.5

    def __init__(self, num):
        self._num = num

    @staticmethod
    def get_weight():
        print("小鸡重量 {0} kg:".format(Chicken.weight))


def run(num):
    print("小鸡 {0} 跑起来:".format(num))


Chicken.run = run
Chicken.run("Num1")
以上方法的适用场景:
- 实例方法:当设计的方法在各个实例之间是相互独立的,操作的属性也是实例属性时,推荐使用实例方法。
- 类方法:当设计的方法需要在不同的实例之间需要共享信息时,那么这个信息对象可以作为类属性。
- 抽象方法:如果一系列的类都包含了相同的业务逻辑,然后将这些逻辑编写进了父类,但实际上又不能保证其中的某些类不会在未来某个时候出现逻辑变更,那么为了保持调用方接口稳定,在不变更方法调用方式的前提下可以使用新功能,此时推荐使用抽象方法。
- 静态方法:当设计的方法与实例、类本身都无关,属于非业务要求的功能性操作时,比如设计一个登录功能,需要为每次登录都记录日志,那么这个日志方法就可以设计为静态方法。
- 动态方法:如果设计的方法需要附加到一个类上临时使用,从面向对象的角度来看,其并不属于类的一部分,这时可以使用动态方法。

三、类的继承

继承是对封装的扩展。当新的类别具备现有类别的特征和行为,并且还有更多属于自身的特点时,那么一般认为新类别是现有类别的一个子类别,现有类别成为父类别。

单一继承

  1. 单一继承:是指只继承了一个类的属性和方法,如下所示:
# -*- coding: UTF-8 -*-

class Poultry:
    def __init__(self, colour):
        self._colour = colour

    def fly(self):
        print("这是父类:poultry的方法")


class Chicken(Poultry):# Chicken类继承了Poultry类,也拥有了Poultry类的属性和方法。
    pass

chicken = Chicken("黄色")
print("访问_colour属性:", chicken._colour)
chicken.fly()

多重继承

  1. 多重继承:是指一个类继承了多个其他类。如果不同基类中有同名的方法,那么子类按照基类继列表的顺序来继承父类(即:继承时,参数的先后顺序),如下所示:
# -*- coding: UTF-8 -*-
class Poultry:
    def __init__(self, colour):
        self._colour = colour

    def fly(self):
        print("这是父类:poultry的方法")

class Chicken:
    def eat(self):
        print("这是父类:Chicken的方法")


class Cock(Poultry, Chicken):
    pass


cock = Cock("黄色")
print("访问_colour属性:", cock._colour)
cock.fly()
cock.eat()

四、类的多态

多态是指子类别与父类别有相同行为,但是表现形式不同。

  1. 当子类中有与父类同名的方法时,调用此方法时仅调用子类中的方法。
  2. ”鸭子“特性:是指一个类不必从另一个类继承,在同一个调用过程中,python解释器不会检查传入参数的类型,传入的参数对象中只要包含了即将执行的方法,程序就能正常运行,如下所示:
# -*- coding: UTF-8 -*-
class Poultry:
    def fly(self):
        print("这是父类:poultry的方法")

class Chicken:
    def eat(self):
        print("这是父类:Chicken的方法")

class Cock(Poultry, Chicken):
    pass

class Duck:
    def fly(self):
        print("这是类:duck的fly方法")

def run(poultry):
    poultry.fly()
cock = Cock()
duck = Duck()
run(cock)
run(duck)
令:python 中的反射机制,是指可以动态的创建对象,也可以动态的获取和设置对象信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值