一、什么是面向对象编程?     

        

        面向对象编程是一种编程范式,它使用“对象”来设计和组织代码,旨在让程序设计更加直观、易于维护。

在面向对象的世界观里,万物皆对象。

  • :是一个对象,有属性(姓名、年龄、身高)和方法(吃饭、走路、说话)。
  • 手机:是一个对象,有属性(品牌、型号、颜色)和方法(打电话、拍照、刷视频)。

核心思想

        面向对象编程就是模拟现实世界。我们先制造一个通用的“模板”(类),定义好这类事物共有的特征(属性)和能做的事(方法)。然后利用这个模板,批量生成一个个实实在在的个体(对象)。

 二、面向对象编程的四大基石

1. 对象
    - 定义:现实世界中存在的具体事物。
    - 举例:你手中的一部具体的“小米手机”就是一个对象,它有特定的颜色,能执行特定的操作。
2. 类
    - 定义:生成对象的模板或蓝图(类似于手机的设计图纸)。
    - 作用:统一界定同类事物的特征与功能。依据这个模板,可以生成无数个独立的实例。
3. 属性
    - 定义:对象具备的特征数据(变量)。
    - 举例:手机的品牌、机身颜色、内存大小。
4. 方法
    - 定义:对象可执行的行为操作(函数)。
    - 举例:手机的拨打电话、拍照、关机重启等功能。

三、定义一个简单的类

        创建一个简单的类,名为phone这个类将包含一些基本属性,例如品牌和型号,以及一些方法。

class phone:

    def __init__(self,brand,model,color):

        self.brand = brand

        self.model = model

        self.color = color

    def call(self):

        print(f"正在使用{self.color}的{self.brand}{self.model}打电话")

phone1 = phone("小米","15","绿色")

phone1.call()

phone2 = phone("华为","p50","黑色")

phone2.call()

输出:正在使用绿色的小米15打电话

正在使用黑色的华为p50打电话

代码解释
        1)class phone:
                class 是用来定义一个类的关键字。类可以看作是一个模板,它定义了对象的属性(变量)和行为(方法)。在这里,我们定义了一个名为 phone(手机)的类。
        2)构造函数 __init__

def __init__(self,brand,model,color):

       self.brand = brand

       self.model = model

       self.color = color

                • __init__ 是类的构造函数,它在创建对象时自动被调用,用来初始化对象的属性。
你可以把它想象成给对象“赋值”的地方。
                • self 是指当前这个对象自己。它允许你访问对象的属性和方法。
                • brand 和 model 和color是传入的参数,用来为每个手机对象设置不同的品牌和型号,比如“绿色小米15”。
        3)方法 call
                def call(self):
                print(f"正在使用{self.color}的{self.brand}{self.model}打电话")
                • call 是类中的一个方法,也就是这个手机对象会有的一个行为。在这里,
                调用这个方法后,会输出一个信息表示手机打电话了。

       4)创建对象并调用方法

phone1 = phone("小米","15","绿色")

phone1.call()

phone2 = phone("华为","p50","黑色")

phone2.call()

输出:正在使用绿色的小米15打电话

正在使用黑色的华为p50打电话

在这个例子中 phone1和phone2是phone类两个不同的对象,分别代表2个不同的手机。

四、定义Girlfriend类

        为了展示类和对象的更多特性,我们将创建一个 Girlfriend 类。这个类将包含一些属性,例
如 name(名字)、age(年龄)和 hobby(爱好),并定义一个 introduce 方法,用于让对
象自我介绍。

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def introduce(self):

        print(f"大家好,我叫{self.name},今年{self.age}岁,我喜欢{self.hobby}。")

xiaohong = Girlfriend("小红", 18, "打篮球")

xiaohong.introduce()

xiaolan = Girlfriend("小蓝", 19, "打羽毛球")

xiaolan.introduce()

输出:

大家好,我叫小红,今年18岁,我喜欢打篮球。

大家好,我叫小蓝,今年19岁,我喜欢打羽毛球。

代码解析:

        1、__init__ 方法用于初始化每个 Girlfriend 对象的 name、age 和 hobby 属性。

        2、调用introduce 方法返回一个字符串,让对象可以“自我介绍”。

        3、我们创建了两个 Girlfriend 对象,xiaohong 和 xiaolan,它们分别代表不同的人物

类的进一步扩展:修改属性和方法

        类不仅可以定义属性和方法,还可以在对象创建后动态修改这些属性。接下来,我们将展
示如何修改对象的属性,以及如何在方法中使用动态参数。

        动态修改属性:

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def introduce(self):

        print(f"大家好,我叫{self.name},今年{self.age}岁,我喜欢{self.hobby}。")

xiaohong = Girlfriend("小红", 18, "打篮球")

xiaohong.age = 29

xiaohong.introduce()

代码解释:

直接访问对象的属性去修改它们的值,我们将 xiaohong 的年龄从 18 改为 29,调用 introduce 方法时,自我介绍的内容也随之改变。

带有参数的方法
        类的方法可以接受参数,以提供更灵活的功能。我们可以修改 introduce 方法,使其接受一个 greeting 参数,以便在不同情况下使用不同的问候语。

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def introduce(self,greeting):

        print(f"{greeting},我叫{self.name},今年{self.age}岁,我喜欢{self.hobby}。")

xiaohong = Girlfriend("小红", 18, "打篮球")

xiaohong.introduce("你好")

代码解释
        在这个例子中:
        introduce 方法现在接受一个额外的参数 greeting,我们可以在调用时传入不同的问候语,使得输出更加灵活
       

五、类的继承

        类可以通过继承(Inheritance)机制从其他类派生出新的类。继承使得新类(子类)能够继承其父类的属性和方法,并可以添加新的功能或覆盖现有的功能。接下来,我们将创建一个继承自 Girlfriend 的新类 LongDistanceGirlfriend,用来表示异地恋女友。

 class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def introduce(self):

        print(f"大家好,我叫{self.name},今年{self.age}岁,我喜欢{self.hobby}。")

class LongDistanceGirlfriend(Girlfriend):

    def __init__(self, name, age, hobby, distance):

        super().__init__(name, age, hobby)

        self.distance = distance

    def introduce(self):

        print(f"大家好,我叫{self.name},今年{self.age}岁,我喜欢{self.hobby},我们相距{self.distance}公里。")

xiaohong = LongDistanceGirlfriend("小红", 18, "打篮球", 1000)

  代码解析:

        LongDistanceGirlfriend 类继承了 Girlfriend 类,意味着它拥有 Girlfriend 的所有属性和方法。
        __init__ 方法通过 super() 调用了父类的构造函数,初始化了 name、age 和 hobby 属性,并且添加了一个新的属性 distance。        

        introduce 方法重写了父类的方法,在调用父类方法的基础上,增加了距离信息。
        ld_gf 是 LongDistanceGirlfriend 类的实例,代表一个异地恋女友。
        调用 introduce 方法时,它会先执行父类的方法,然后再添加上特有的距离信息。

六、类的特殊方法

        每个 Python 类都可以有一些特殊方法,这些方法的名字通常是以双下划线开头和结尾的,
比如 __init__。这些方法允许我们为类对象定义特殊的行为,让它们的表现更像内置的数据
类型。例如,__str__ 是一个特殊方法,它用于定义对象被打印或转换为字符串时的输出内
容。

        如果你没有定义 __str__ 方法,当你试图打印一个类的实例时,Python 会使用默认的字符
串表示形式。这个默认的输出通常是对象的类型名和对象在内存中的地址,如下:

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

gf = Girlfriend(name="小红", age=25, hobby="阅读")

print(gf)

输出:<__main__.Girlfriend object at 0x000001A1CB8A06D0>

自定义字符串表示: 通过定义 __str__ 方法,你可以控制当使用 print 函数或 str() 
函数来打印对象时,显示的具体内容。例如:

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def __str__(self):

        return f"大家好,我叫{self.name},今年{self.age}岁,我喜欢{self.hobby}。"

gf = Girlfriend(name="小红", age=25, hobby="阅读")

print(gf)

输出  大家好,我叫小红,今年25岁,我喜欢阅读。

定义 __eq__ 方法来比较对象

        在 Python 中,所有的对象都可以使用 == 运算符进行比较。默认情况下,== 运算符比较
的是对象的内存地址,也就是说,它比较的是两个对象是否是同一个对象。如果你想让两
个对象的比较更具意义(例如比较对象的内容),你需要自定义 == 运算符的行为。

 自定义 __eq__ 方法
        要改变 == 运算符的默认行为,你需要在类中定义一个特殊的方法 __eq__。这个方法会在
比较两个对象时被自动调用。其定义如下:

def __eq__(self, other):
        # 自定义比较逻辑
        self 是当前对象。
        other 是你用 == 运算符比较的另一个对象。
实现自定义比较
        我们定义一个 Girlfriend 类,并为它实现了 __eq__ 方法。这个方法用于比较两个
Girlfriend 对象的相等性。

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def __eq__(self, other):

        if isinstance(other, Girlfriend):

            return self.name == other.name and self.age == other.age and self.hobby == other.hobby

        return False

代码解释:

        __eq__ 方法首先检查 other 是否是 Girlfriend 类的实例。如果不是,就返回 False,因为我们只能比较同类的对象。
        如果 other 是 Girlfriend 的实例,方法会比较 self 和 other 的 name、age 和 hobby 属性。
        如果所有这些属性都相等,那么两个对象就被认为是相等的,方法返回 True;否则返回 False。

实际应用

class Girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

    def __eq__(self, other):

        if isinstance(other, Girlfriend):

            return self.name == other.name and self.age == other.age and self.hobby == other.hobby

        return False

gf1 = Girlfriend(name="小红", age=25, hobby="阅读")

gf2 = Girlfriend(name="小红", age=25, hobby="阅读")

print(gf1 == gf2) # 输出: True

gf3 = Girlfriend(name="小蓝", age=25, hobby="阅读")

print(gf1 == gf3) # 输出: False

输出:

True

False

代码解释:

        gf1 == gf2 会调用 gf1.__eq__(gf2),并比较 gf1 和 gf2 的所有属性。如果它们的name、age 和 hobby 都相等,结果就是 True。
        gf1 == gf3 会调用 gf1.__eq__(gf3),比较 gf1 和 gf3 的所有属性。由于这些属性不同,所以结果是 False。

七、类的访问控制

        在 Python 中,类的访问控制主要依靠命名约定,来提示开发者哪些属性和方法不应该被外
部代码直接访问或修改。Python 没有像其他语言(如 Java、C++)那样严格的访问控制机制,它通过单下划线(_)和双下划线(__)来实现一定程度的封装性。

1. 单下划线(_):受保护的属性和方法
        • 命名方式:属性或方法的名称以单下划线 _ 开头。
        • 目的:它是一种提示,告诉其他开发者这些属性和方法是类的内部实现,不建议直
接访问或修改。
        • 实际效果:这种方式并没有真正阻止外部访问。你仍然可以从类的外部访问和修改
这些属性和方法,但这样做是不推荐的,因为它们被认为是类的内部实现部分,随时可能被修改。

class Example:

    def __init__(self):

        self._protected_attribute = 58

obj = Example()

print(obj._protected_attribute) # 可以访问,但不建议

obj._protected_method() # 可以调用,但不建议

2. 双下划线(__):私有的属性和方法
        • 命名方式:属性或方法的名称以双下划线 __ 开头。
        • 目的:双下划线的属性和方法被认为是私有的,用于更严格的封装,防止外部代码
直接访问。
        • 实际效果:Python 通过名称改编(name mangling)机制将以双下划线开头的属性
和方法的名称改为 _类名__属性名 或 _类名__方法名,从而实现一定程度的隐藏,
避免外部直接访问它们。

class Example:

    def __init__(self):

        self.__private_attribute = 58 # 私有属性

    def __private_method(self): # 私有方法

        print("This is a private method.")

    def public_method(self):

        print(self.__private_attribute) # 类内部可以访问

        self.__private_method() # 类内部可以调用

obj = Example()

print(obj.__private_attribute) # 无法直接访问,AttributeError

obj.__private_method() # 无法直接调用,AttributeError

代码解释:

        在这个例子中,__private_attribute 和 __private_method 无法被类外部直接访问,因为它们
的名称在底层被改编为 _Example__private_attribute 和 _Example__private_method。这是一
种保护机制,使得它们对外部代码不可见,但仍然可以通过类内部的其他方法(public_method)来访问或调用。

总结:
        单下划线 _:表示“受保护的”属性或方法,不建议外部直接访问,但没有实际阻止
访问。
        双下划线 __:表示“私有的”属性或方法,通过名称改编机制在外部代码中无法直接
访问,提供更强的封装性。

扩展:使用访问器(getter)和修改器(setter)

        在面向对象编程中,对象的属性(即类中的变量)通常是私有的或受保护的,这意味着外
部代码不能直接访问或修改它们。这时候就需要通过访问器(getter)和修改器(setter)来控制这些属性的访问和修改。

        访问器(getter):这是一个用来“获取”对象属性值的方法。通过访问器,你可以从
对象中读取某个属性的值,但不会直接访问该属性本身。
        修改器(setter):这是一个用来“设置”对象属性值的方法。通过修改器,你可以更
改对象中的属性值,同时可以在设置值之前进行一些检查或处理。

作用:
        1. 数据验证:你可以在修改器中对属性值进行验证,确保设置的值是有效的。例如,年龄不能是负数或超过某个上限值。
        2. 封装:访问器和修改器提供了一层抽象,隐藏了对象内部属性的实现细节。这意味着你可以随时修改属性的实现方式,而不必担心影响到使用这个类的外部代码。
        3. 控制访问权限:你可以通过只提供访问器而不提供修改器来实现只读属性,或者根据需要提供修改器以允许修改属性。

class User:

    def __init__(self,relationship_status):

        self._relationship_status = None

        self.set_relationship_status(relationship_status)

    def get_relationship_status(self):

        return self._relationship_status

    def set_relationship_status(self, status):

        if status not in ["single", "in a relationship", "married"]:

            raise ValueError("Invalid relationship status.")

        self._relationship_status = status

user1 = User("single")

print(user1.get_relationship_status())

user2 = User('married')

print(user2.get_relationship_status())

user1.set_relationship_status('in a relationship')

print(user1.get_relationship_status())

user2.set_relationship_status('complicated')

输出:

single

married

in a relationship

    user2.set_relationship_status('complicated')

  File "d:\python\python-openclow\tempCodeRunnerFile.py", line 212, in set_relationship_status

    raise ValueError("Invalid relationship status.")

ValueError: Invalid relationship status.

代码分析:

         __init__ 方法是 Python 类的构造函数,它会在我们创建 User 对象时自动调用。
        这里,self._relationship_status = None 初始化了一个受保护的属性(以 _ 开头的属性通常视为私有,外部代码不应直接访问它)。
        然后通过调用 self.set_relationship_status(relationship_status) 来设置初始值。注意这里并没有直接设置,而是通过 set_relationship_status 这个修改器来进行修改,目的是为了控制输入的数据是否合法。

访问器 get_relationship_status:
        访问器(也叫 getter)是用于从类中获取私有或受保护属性的值。
        在这个例子中,get_relationship_status 返回 _relationship_status 的值,让外部代码可以通过这个方法来获取用户的关系状态。

修改器 set_relationship_status:
        修改器(也叫 setter)负责控制对类中属性的修改。
        在这个例子里,set_relationship_status 方法首先定义了一个有效的状态列表valid_statuses,其中包括 'single'(单身)、'in a relationship'(恋爱中)、'married'(已婚)。

        当你想修改关系状态时,修改器会检查传入的 status 是否在 valid_statuses 列表中。
        如果状态有效,self._relationship_status = status 将更新属性的值。
        如果状态无效,代码会抛出 ValueError 异常,并提供一个错误信息,告知用户输入的状态无效。

八、类的高级用法

      在面向对象编程中,类是用来创建对象的模板。类的高级用法允许我们在不改变类本身代码的情况下,添加、修改或扩展其功能。今天,我们要讨论的一个强大工具是 类装饰器。

类装饰器:

        类装饰器是一个函数,它接收一个类作为输入,并返回一个新的类或修改后的类。使用类
装饰器,我们可以在不改变原始类代码的情况下,对类的行为进行增强或改变。

添加日志记录的类装饰器
我们将编写一个装饰器,每次创建类的实例时,都会打印出一些日志信息

def log_creation(cls):

    class wrapped(cls):

        def __init__(self, *args, **kwargs):

            print(f"创建对象:{args[0]}")

            super().__init__(*args, **kwargs)

    return wrapped

@log_creation

class girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

gf = girlfriend("小红", 18, "打篮球")

返回:创建对象:小红

代码解释:

        装饰器 log_creation 本质上是一个函数,它接收一个类作为参数,并返回一个新的
类。

        这个装饰器通过继承的方式创建了一个新的类 Wrapped,并且这个 Wrapped 类继
承了原始类 Girlfriend。

Wrapped 类的__init__方法
        在 Wrapped 类中,__init__方法被重写。每次创建对象时,都会执行这个__init__方法。
        print(f"创建对象: {args[0]}") 这一行代码用来打印日志信息。args[0]指的是传入的第
一个参数,即 Girlfriend 对象的名字,比如"Alice"。
        然后通过 super().__init__(*args, **kwargs)调用了父类的__init__方法,确保原始类
Girlfriend 的初始化逻辑不受影响。

 装饰器的作用
        当你用@log_creation 修饰 Girlfriend 类时,Girlfriend 类就被装饰器替换成了
Wrapped 类。
        现在,当你创建 Girlfriend 对象时,实际上是通过 Wrapped 类创建的,它会自动执
行日志打印功能。

装饰器工厂

        我们通过装饰器工厂和装饰器函数的方式来实现对类实例化过程中进行权限检查。这个方
法允许我们在创建类的实例之前检查用户是否有权限创建实例。

 装饰器工厂
        作用: 装饰器工厂的作用是返回一个装饰器函数。装饰器工厂使我们能够在装饰器中使用参数,从而创建更具灵活性的装饰器。
特点:

        • 装饰器工厂本身不会直接用于装饰类或函数,它返回一个实际的装饰器函数。
        • 装饰器工厂允许我们在装饰器中传递参数,从而改变装饰器的行为。
使用场景:
        • 当我们需要在装饰器中使用动态参数时,例如根据权限控制装饰器的行为,装饰器工厂非常有用。
2. 类装饰器
        作用: 类装饰器直接作用于类,能够修改类的行为。它接受一个类作为参数,返回一个经
过修改的新类。
特点:
        • 类装饰器对类的功能进行增强或添加额外逻辑(例如权限检查)。
        • 类装饰器与函数装饰器不同,主要用于修改类的行为而不是函数的行为。
使用场景:
        • 需要在类创建时添加额外逻辑(如权限检查)时,可以使用类装饰器

def require_permission(permission):

    def log_creation(cls):

        class wrapped(cls):

            def __init__(self, *args, **kwargs):

                if permission!="admin":

                    raise PermissionError("没有权限创建对象")

                print(f"创建对象:{args[0]}")

                super().__init__(*args, **kwargs)

        return wrapped

    return log_creation

@require_permission("admin")

class girlfriend:

    def __init__(self, name, age, hobby):

        self.name = name

        self.age = age

        self.hobby = hobby

gf = girlfriend("小红", 18, "打篮球")

执行结果
因为我们假设用户有权限,所以代码将顺利执行,否则控制台将输出类似以下内容:没有权限创建对象

代码解释:

require_permission(permission)
        • 这是一个装饰器生成函数,它接受一个参数 permission,即我们要检查的权限。
        • 它返回了一个装饰器函数 decorator,专门用来装饰类。
def log_creation(cls):
        • 这个函数是装饰器的实际实现,它接受一个类 cls 作为参数。
        • decorator 函数的作用是用它内部定义的一个新类 Wrapped 替换原来的类 cls,从而增加新的功能(权限检查)。

permission!="admin":

检查permission 是否等于admin,如果不等于admin 则返回 没有权限创建对象。

更多推荐