python面向对象解析(简单易懂版)

Python从设计之初就已经是一门面向对象的语言,正因如此,在Python中创建一个类和对象是很容易的。面向对象编程(Object-Oriented Programming, OOP)是一种程序设计范式,它将现实世界中的实体抽象成类,并通过实例化这些类来创建具体对象。在Python中,面向对象有三个核心概念:封装、继承和多态;我们在此篇分开讲解。

目录

面向对象技术简介

一、类(class)&方法(method)

1、实例方法(Instance Method)

2、类方法(Class Method)

3、静态方法(Static Method)

二、对象(Object)

实例化

_ _init_ _()(构造方法)

self是什么

三、封装

概念

成员保护

单下划线开头的变量(_var): 

双下划线开头的变量(__var):

@property装饰器创建只读属性:

四、继承

五、多态


面向对象技术简介

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 方法:类中定义的函数。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • 继承:即一个子类继承父类的字段和方法。继承也允许把一个子类的对象作为一个父类对象对待。
  • 实例化:创建一个类的实例,类的具体对象。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

一、类(class)&方法(method)

0、类的定义

类是创建对象的蓝图或模板,它描述了一组具有相同属性和行为的对象集合。通过定义类,可以设定对象的数据结构(即属性)以及操作数据的方法。

举个例子:

class ClassName:
    # 类体部分
    def __init__(self, attr1, attr2):  # 初始化方法(构造函数)
        self.attr1 = attr1  # 定义实例属性
        self.attr2 = attr2

    def method_name(self, arg1):  # 定义类里的方法
        # 对象的方法实现
        pass
除了类里定义的方法,还有实例方法(Instance Method)、类方法(Class Method)、静态方法(Static Method),我们逐一拓展了解一下:

1、实例方法(Instance Method)

实例方法与特定的类实例相关联,它默认接收一个隐含的参数self作为第一个参数,这个self指向调用该方法的对象本身。

举个生活例子:假设我们有一个“手机”类,它有一个make_call的实例方法。当你拥有一部具体的手机(例如iPhone),你可以通过这部iPhone拨打电话(调用make_call方法)。这里的iPhone就是类“手机”的一个实例。

class Phone:
    def __init__(self, brand):
        self.brand = brand
    # 实例方法
    def make_call(self, number):
        print(f"使用 {self.brand} 打电话给: {number} ...")
my_phone = Phone("iPhone")
my_phone.make_call("12345678")  # 调用实例方法 # 输出结果:使用 iPhone 打电话给: 12345678 ...

在这个例子中,make_call()是一个实例方法,因为它依赖于具体的my_phone实例(例如"iphone"),并通过self引用了实例的brand属性。

2、类方法(Class Method)

类方法与类关联而不是实例,它需要使用装饰器@classmethod进行标识,并且其第一个参数通常命名为cls,代表调用它的类本身。

举个生活例子:继续以手机为例,如果有一个类方法get_default_brand,用于返回该品牌手机的默认型号。不论你拥有哪款手机,都可以通过手机品牌类直接获取其默认型号。

class Phone:
    @classmethod
    def get_default_brand(cls):
        return "HUAWEI MATE 60 Pro"

# 不需要实例化Phone类就可以调用类方法
default_model = Phone.get_default_brand()
print(default_model) # 输出结果:HUAWEI MATE 60 Pro

在上面的例子中,get_default_brand()是一个类方法,它可以用来返回该品牌手机的默认型号,无需先创建一个实例就可以直接通过类名调用。

3、静态方法(Static Method)

静态方法不依赖于类或类实例,它与类的关系比类方法更弱,更像是独立的函数,但为了组织代码方便,将它们放在类的命名空间下。静态方法使用装饰器@staticmethod进行标识,不需要任何特殊的参数。

举个生活例子:假设我们在手机商店类中有一个静态方法calculate_price,用来计算购买多部手机的总价格,无论买的是什么品牌的手机,也不需知道具体的手机实例,只和购买的数量和单价有关。

class MobileShop:
    @staticmethod
    def calculate_price(quantity, unit_price):
        return quantity * unit_price
# 不需要实例化MobileShop类就可以调用静态方法
total_cost = MobileShop.calculate_price(2, 8000)
print(total_cost)  # 输出:16000
上述例子中的 calculate_price()就是一个静态方法,它并不需要知道任何关于MobileShop类实例的信息,只是提供了一个简单的计算功能,与类本身无关。
小结
1.定义形式上:
类方法和静态方法都是通过装饰器实现的,实例方法不是;实例方法需要传入self参数,类方法需要传入cls参数,而静态方法不需要传self或者cls参数。
2. 调用方式上:
实例方法只能通过实例对象调用;类方法和静态方法可以通过类对象或者实例对象调用,如果是使用实例对象调用的类方法或静态方法,最终都会转而通过类对象调用。
3. 应用场景:
实例方法使用最多,可以直接处理实例对象的逻辑;类方法不需要创建实例对象,直接处理类对象的逻辑;静态方法将与类对象相关的某些逻辑抽离出来,不仅可以用于测试,还能便于代码后期维护。
实例方法和类方法,能够改变实例对象或类对象的状态,而静态方法不能

二、对象(Object)

实例化

为类创建的具体实例称为类对象。通过调用类名后跟括号()并传入初始化方法所需的参数来创建类对象。

  • 类对象支持两种操作:属性引用和实例化。
  • 属性引用使用和 Python 中所有的属性引用一样的标准语法:obj.name。
  • 类对象创建后,类命名空间中所有的命名都是有效属性名。
class MyClass:
    """一个简单的类实例"""
    i = 123
    def func(self):
        return 'hi,are you ok'
# 实例化类
x = MyClass()
# 访问类的属性和方法
print("MyClass 类的属性 i 为:", x.i)
print("MyClass 类的方法 f 输出为:", x.func())

#以上创建了一个新的类实例并将该对象赋给局部变量 x,x 为空的对象。
#输出:
'''
MyClass 类的属性 i 为: 123
MyClass 类的方法 f 输出为: hi,are you ok
'''
类中的数据,和实例中的数据,是什么关系?
访问实例数据时,会先从实例的 命名空间 中寻找, 如果实例的命名空间 没有 ,就从类的命名空间 中寻找。
class A:
    N=0
    def xy(self):
        A.N=123
a=A()
b=A()
print(a.N) # 输出结果:0
print(b.N) # 输出结果:0
print(A.N) # 输出结果:0
#此时a.N和b.N的值都为0,都是使用类的命名空间,调用了类的属性N=0

b.N=100 # b创建了N=100,接下来b调用的N是 b实例的命名空间里的N
print(a.N) # 输出结果:0
print(b.N) # 输出结果:100
print(A.N) # 输出结果:0

a.xy() # a实例调用xy()方法,修改了类的命名空间里的属性N=123
print(a.N) # 输出结果:123
print(b.N) # 输出结果:100
print(A.N) # 输出结果:123
#此时a使用的是类的命名空间,a.N、A.N输出的都是123,b使用的是自己实例的命名空间

_ _init_ _()(构造方法

类定义了 _ _init_ _() 方法,类的实例化操作就会自动调用 _ _init_ _() 方法。_ _init_ _() 方法可以有参数,参数通过 _ _init_ _() 传递到类的实例化操作上。例如:

class number_data:
    def __init__(self, int_data, float_data):
        self.i = int_data
        self.f = float_data
x = number_data(3, 4.5)
print(x.i, x.f)   # 输出结果:3  4.5
dataclass 饰器, 自动为 class 添加 了很多 特殊 方法 ,包括 __init__,使用该装饰器 不需要 定义 __init__也能实现功__init__的功能,使用规则如下例:
import dataclasses
@dataclasses.dataclass
class People:
    name: str = ""  # 前提:必须为属性,声明类型,否则会报错
    age: int = 0
    sex: str = ""
    def show(self):
        print(self.name)
        print(self.age)
        print(self.sex)
a = People("小石", 19, "男")
a.show() # 输出结果:小石 19 男

self是什么

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例该参数的名称是 self。

#例1
class myclass:
    def pr(self):
        print(self)
        print(self.__class__)
t = myclass()
t.pr()
#输出
'''
<__main__.myclass object at 0x000002392516FFD0>
<class '__main__.myclass'>
'''

#例2
class people:
    # 定义基本属性
    name = ''
    age = 0
    # 定义构造方法
    def __init__(self, n, a, ):
        self.name = n
        self.age = a
    def speak(self):
        print("%s 说: 我 %d 岁。" % (self.name, self.age))

# 实例化类
p = people('小石', 18)
p.speak() # 输出结果:小石 说: 我 18 岁。
'''
在类的内部,使用def关键字来定义一个方法,与一般函数定义不同,
类方法必须包含参数self, 且为第一个参数,self 代表的是类的实例。
'''

从上述例1执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。self 不是 python 关键字,我们把他换成别的字符串也是可以正常执行的:

class myclass:
    def pr(me):
        print(me)
        print(me.__class__)
t = myclass()
t.pr()
#输出
'''
<__main__.myclass object at 0x000002392516FFD0>
<class '__main__.myclass'>
'''

三、封装

概念

封装是把数据(属性或字段)和对这些数据的操作(方法)捆绑在一起,并隐藏内部实现细节的过程。通俗地讲,封装就像一个“黑箱”,我们只需要知道怎么使用这个箱子(调用接口),而不需要了解箱子内部是如何工作的。在Python中,类可以定义私有变量(通常通过双下划线前缀_ _var来表示),外部无法直接访问,只能通过类提供的公共方法进行操作,确保对象中的数据安全。

举个生活中的例子来解释封装:

遥控器内部有很多复杂的电子元件和电路(这些是“数据”),但我们并不需要知道它们是如何工作的。我们只需要通过按钮(“方法”)来操作它,比如按下开关按钮、调节音量等。

class RemoteController: #定义遥控器类
    # 内部复杂的数据结构(类似电子元件和电路)
    __battery_level = 100  # 私有属性,外部无法直接访问或修改
    def __init__(self):
        self.__turn_on_tv()  # 初始化时打开电视
    # 对外提供的操作接口(方法)
    def turn_on(self):
        self.__operate_tv("打开电视机")
    def turn_off(self):
        self.__operate_tv("关闭电视机")
    def change_volume(self, volume):
        self.__set_volume(volume)
    # 内部实现细节(不直接暴露给外部)
    def __turn_on_tv(self):
        print("TV is turning on...")
    def __operate_tv(self, action):
        print(f"Operating TV: {action}")
    def __set_volume(self, volume):
        if 0 <= volume <= 100:
            print(f"Setting volume to {volume}")
        else:
            print("Invalid volume level!")

# 使用者视角:
remote = RemoteController()
remote.turn_on()  # 按下开关键
remote.change_volume(50)  # 调整音量至50

在这个例子中,定义了一个名为RemoteController的遥控器类,该遥控器类具有以下功能:

  • 1、内部具有复杂的数据结构,包括私有属性__battery_level表示电池电量。
  • 2、在初始化时,会自动调用__turn_on_tv()方法打开电视。
  • 3、提供对外的操作接口,包括打开电视(turn_on)、关闭电视(turn_off)和调节音量(change_volume)。
  • 4、内部实现了具体的电视操作细节,包括打开电视(__turn_on_tv)、操作电视(__operate_tv)和设置音量(__set_volume)。

其中,__turn_on_tv()、__operate_tv()和__set_volume()方法为私有方法,只能在类的内部调用,不能直接从外部访问或调用。而turn_on()、turn_off()和change_volume()方法为公有方法,可以从类的外部调用,提供了对外的操作接口。

成员保护

1)  _ 下划线开头的变量,受保护的成员,不建议直访问和修改,允许在类内部进行访问和修改
2)_ _ 双下划线,受保护的成员,不能直接访问和修改,允许在本类内部进行访问和修改
3)@property 装饰器:是一个函数伪装成属性,调用时不需要加括号;我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。

单下划线开头的变量(_var): 

生活例子:假设你有一本私人日记,你不想让所有人都看到,但允许家人在必要时查看。日记本就像是一个类,而日记内容是其中的一个变量 _content。虽然不鼓励外人直接翻看你的日记,但在家庭内部,父母或兄弟姐妹可以出于关心询问并阅读。

class Diary:
    def __init__(self, content):
        self._content = content
    def share_with_family(self):
        return self._content  # 家庭成员可以通过这个方法来读取日记内容
daliy=Diary("今天天气真好!")
print(daliy.share_with_family()) # 输出结果:今天天气真好!
# 虽然按照约定不应该直接访问,但仍然可以这样做
print(daliy._content) # 输出结果:今天天气真好!

双下划线开头的变量(__var):

 生活例子:现在设想你有一个保险箱,它有一个非常私密的四位数密码 __password,除了你自己,任何人都不能知道或更改。即使是在保险箱内部,密码也不会被轻易公开,而是通过一种安全的方式来验证和修改密码。

class Safe:
    def __init__(self, password):
        self.__password = password
    #定义验证修改密码方法
    def verify_and_change_password(self, old_password, new_password):
        if self.__password == old_password:
            self.__password = new_password
            print(f"密码修改成功,新密码为:{self.__password}")
        else:
            print("密码修改失败,旧密码输入有误.")

obj=Safe("123456")
print(obj.__password)
'''直接访问会报错
    print(obj.__password)
AttributeError: 'Safe' object has no attribute '__password'
'''
# 但实际上仍可通过原名访问,但这违反了封装原则
print(obj._Safe__password)  # 输出: 123456,但这并不是推荐的做法

obj.verify_and_change_password("123456", "654321")#修改密码 输出 密码修改成功,新密码为:654321
obj.verify_and_change_password("123456", "654321")#修改密码 输出 密码修改失败,旧密码输入有误.
obj.verify_and_change_password("654321", "111111")#修改密码 输出 密码修改成功,新密码为:111111

# 强行修改
obj.__password = "123321"
print(obj.__password) #强行修改后可以直接访问到属性值,
#这里尝试直接修改__password属性时,
#Python解释器会将__password改写为 _Safe__password(即在类名和原属性名间插入一个下划线),所以上述代码依然能够改变密码:
#但是,直接修改私有变量违反了封装原则,应当避免这样做。在设计类时,采用getter和setter方法来控制对私有属性的访问是更好的做法。

@property装饰器创建只读属性:

1.修饰方法,使方法可以像属性一样访问。

2.与所定义的属性配合使用,这样可以防止属性被修改。

由于python进行属性的定义时,没办法设置私有属性,因此要通过@property的方法来进行设置。这样可以隐藏属性名,让用户进行使用的时候无法随意修改。

生活例子:想象一下你拥有一台智能冰箱,冰箱有一个显示屏显示当前内部温度。为了保证冰箱运行的安全和稳定,你不希望别人随意修改设定温度,所以将温度设置为只读属性,只能查看不能直接修改。

class SmartFridge:
    def __init__(self, current_temperature):
        self.__current_temperature = current_temperature #初始化 当前温度 属性值
    @property
    def temperature(self):  # 这是一个只读属性,用于获取当前温度
        return self.__current_temperature
    # 可以定义一个方法来调整温度,但不是直接修改temperature属性
    def set_temperature(self, new_temperature):
        if 0 <= new_temperature <= 10:  # 假设合理温度范围为0-10度
            self.__current_temperature = new_temperature
            print(f"温度设置为{new_temperature}")
        else:
            print("温度设置无效")

fridge = SmartFridge(3)
print(fridge.temperature)  # 输出:5
fridge.set_temperature(6)  # 正确做法是通过set_temperature方法调整温度
print(fridge.temperature)  # 输出:6
fridge.set_temperature(11) # 输出:温度设置无效

fridge.temperature = 5
'''这将引发错误,因为我们已将其设置为只读属性
    fridge.temperature = 5
AttributeError: can't set attribute 'temperature'
'''

下划线和双下划线开头的命令方式,只是一种约定,没有强制作用。总结来说,遵循这些下划线约定有助于提高代码质量、可读性和维护性。虽然它们不是强制性的,但是为了遵循最佳实践并编写易于理解和协作的代码,建议在开发过程中遵循这些约定。

四、继承

继承是面向对象编程中的一个核心概念,它允许创建一个新类(称为子类或派生类),该类可以从已存在的类(称为父类或基类)中获取属性和方法。通俗来说,继承就像是现实生活中的一种“模板”或者“蓝图”的应用。

生活例子: 假设我们有一个基础的交通工具类,其中包含了所有交通工具共有的属性(如颜色、速度等)和方法(如启动、停止、加速等)。现在,我们创建一个新的汽车类,一个新的自行车类,分别都是特殊的交通工具,以及创建一个功能类。代码展示继承、多继承、重写父类方法如下:

#定义一个交通工具类
class Transportation:
    def __init__(self, color):
        self.color = color
    def start(self):
        print("启动")
    def stop(self):
        print("停止")
    def accelerate(self):
        print("踩油门加速")

class func:
    def func1(self, name):
        print(f"{name}载人出行")

#单继承
class Car(Transportation):
    def __init__(self, color, wheels):
        super().__init__(color) # 使用 super() 调用父类的 __init__ 方法
        self.wheels = wheels # 为 Car 类添加 车轮 属性
    def horn(self):
        print(f"{self.color}汽车{self.wheels}个轮子")
        print("汽车滴滴响")

car=Car("红色", 4)
car.start() #继承父类的方法,输出:启动
car.stop() #继承父类的方法,输出:停止
car.accelerate() #继承父类的方法,输出:踩油门加速
car.horn() #继承父类的方法,输出:红色汽车4个轮子

#多继承+重写父类方法
class bicycle(Transportation,func):
    def __init__(self, color, wheels):
        super().__init__(color) # 使用 super() 调用父类的 __init__ 方法
        self.wheels = wheels # 为 bicycle 类添加 车轮 属性
        print(f"{self.color}自行车{self.wheels}个轮子")
    def accelerate(self):
        print("使劲踩踏加速")

bic=bicycle("黑色", 2) #实例化类,输出:黑色自行车2个轮子
bic.start() #继承父类的Transportation方法,输出:启动
bic.stop() #继承父类Transportation的方法,输出:停止
bic.func1("自行车") #继承父类func方法,输出:自行车载人出行
bic.accelerate() #重写父类Transportation方法,输出:使劲踩踏加速

五、多态

 多态意味着同一个接口可以有不同的表现形式。在OOP中,这意味着子类可以覆盖或重写父类的方法,从而使得同一消息可以根据发送的对象类型产生不同的结果。同时,由于Python支持鸭子类型(Duck Typing),即“如果它走起路来像鸭子,叫起来也像鸭子,那它就是鸭子”,所以不同类型的对象只要具有相同的方法签名,就可以被同样的方式调用。

生活例子: 假设我们有一批电器设备,它们都有一个“开机”功能,但是具体怎么开机取决于设备的类型:

  1. 电视(TV):按下遥控器上的电源按钮,电视就会开启并显示画面。
  2. 电脑(computer):按一下主机上的开机键,电脑会启动并显示画面。
  3. 电灯(Light):旋动开关或者通过智能灯泡APP点击开灯按钮,电灯亮起。

在程序设计中,我们可以抽象出一个ElectricalDevice基类,其中有一个通用的turn_on()方法。然后定义TVcomputerLight三个子类,分别重写turn_on()方法以实现各自设备的开机行为。当调用任意一个设备实例的turn_on()方法时,执行的就是对应类型的开机操作。这就是多态的体现——通过统一的接口(方法名),实现了不同对象的不同表现形式。

class TV(ElectricalDevice):
    def turn_on(self):
        print("按下遥控器上的电源按钮,电视就会开启并显示画面")

class Light(ElectricalDevice):
    def turn_on(self):
        print("按下墙上的开关,灯就会亮")

class computer(ElectricalDevice):
    def turn_on(self):
        print("按一下主机上的电源按钮,电脑就会开机")

def turn_on(devices):
    devices.turn_on() # 传入的对象不同,turn_on()方法对应的操作和现象也不同

turn_on(TV()) # 输出:按下遥控器上的电源按钮,电视就会开启并显示画面
turn_on(Light()) # 输出:按下墙上的开关,灯就会亮
turn_on(computer()) # 输出:按一下主机上的电源按钮,电脑就会开机
  • 多态保证了代码的灵活性
  • 忽略对象是实际类型,而是以胜任的方式充当任意类型,一个对象可以以不同的形态去呈现
  • 多态是方法的多态,属性没有多态
  • 多态的存在有 2 个必要条件:继承、方法重写

总结来说,面向对象编程的核心在于通过模拟现实世界的实体和关系,建立模型,并利用封装、继承和多态等机制组织代码,使程序更加清晰、模块化和易于维护。希望这篇文章能帮助友友们快速理解弄懂面向对象。

  • 37
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值