Python面向对象

面向对象编程(OOP)基础

面向对象编程(Object-Oriented Programming,OOP)是一种强大的编程范式,它将数据和操作数据的方法封装为对象,通过类的继承和多态实现代码复用和系统扩展。

目录

面向对象编程(OOP)基础

1. 类与对象:基础结构

2. 继承:创建层次结构

3、多继承:代码复用的 “双刃剑”

 概念

4、MRO(方法解析顺序)

 核心作用:

5. 多态:统一接口,不同实现

6、公有、私有与保护属性

1,公有属性与方法

2,私有属性与方法

3, 保护属性

7.类的成员

1、类属性:类与实例的 “共享与独立”

2、类方法:操作类本身的工具

3、静态方法:独立功能

8、动态添加类成员

1、动态添加实例属性与方法

1. 动态添加实例属性

2. 动态添加实例方法

2、动态添加类属性与方法

1. 动态添加类属性

2. 动态添加类方法

3. 动态添加静态方法

9.Python 中的属性操作

hasattr:判断对象是否有指定属性​

setattr:为对象设置属性​

getattr:获取对象的属性值​

delattr:删除对象的属性​

10.property:属性封装

 1. 使用装饰器语法

2. 使用 property 类

3.property 的优势

 


1. 类与对象:基础结构

类是抽象的, 封装数据以及数据对象的操作,定义了对象的属性和方法。

对象是类的实例,是具体的, 有具体数据。


class Rectangle:
    def __init__(self,long,wide):
        
         # 实例属性
        self.long = long
        self.wide = wide

     # 实例方法
    def girth(self):

        return (self.long + self.wide) * 2

    def area(self):
        return self.long * self.wide

    def set_data(self,l = None,w = None):
        self.long = l if l is not None else self.long
        self.wide = w if w is not None else self.wide

# 创建对象
l1 = Rectangle(10,20)
# 访问属性和方法
print(l1.long,l1.wide,l1.girth())
print(l1.area())
l1.set_data(20)
print(l1.long,l1.wide,l1.girth())
print(l1.area())

2. 继承:创建层次结构

继承允许一个类继承另一个类的属性和方法,提高代码复用性。

# 父类
class Animal(object):
    def __init__(self,age):
        self.age = age

    def __str__(self):
        return "动物可以叫"


# 子类
class Dog(Animal):
    def __init__(self, age,height):
        super().__init__(age)
        self.height = height

    def __str__(self):
        return "dog"

    def speak(self):
        print("汪汪")

# 子类
class Aqi(Dog):
    def __init__(self, age, long, height):
        super().__init__(age, height)
        self.long = long

    def __str__(self):
        return "aqi"

    def speak(self):
        super().speak()
        print("嗷嗷叫")



a1 = Animal(6)
print(a1.age,a1)

print("=======")
d1 = Dog(9,0.4)
# 使用继承
print(d1.age,d1.height)
print(d1)
d1.speak()

print("=======")
aq1 = Aqi(10,1.0,0.6)
print(aq1.age,aq1.height,aq1.long)
aq1,aq1.speak()

3、多继承:代码复用的 “双刃剑”

 概念

多继承指一个子类可以同时继承多个父类,语法为 class 子类(父类1, 父类2, ...) 。它的优势是能让子类整合多个父类的属性和方法,快速复用不同来源的功能;但也因多个父类可能存在同名方法、属性,引发 “菱形继承” 等冲突问题,增加代码复杂度。

class Father:
    def __init__(self,money):
        self.money = money


    def get_money(self):
        print(self.money)

class Mother:
    def __init__(self,color):
        self.color = color


    def get_color(self):
        print(self.color)


class Son(Father,Mother):
    def __init__(self, money,color):
        # super().__init__(money)


        Father.__init__(self,money)
        Mother.__init__(self,color)


s1 = Son(100,"red")
s1.get_money()
s1.get_color()

4、MRO(方法解析顺序)

 核心作用:

MRO(Method Resolution Order )即方法解析顺序,它定义了多继承子类在查找属性、方法时,遍历父类的顺序规则。Python 中通过 __mro__ 属性或 mro() 方法可查看,其遵循 C3 算法,确保父类方法查找不重复、不冲突,解决多继承的 “方法调用歧义” 问题。


class B1:
    pass
    # def __str__(self):
    #     return "b1"

class B2:
    pass
    def __str__(self):
        return "b2"


class B(B1,B2):
    pass
    # def __str__(self):
    #     return "b"

class C:
    pass
    def __str__(self):
        return "c"

class A(B, C):
    pass
    # def __str__(self):
    #     return "A"

a = A()
print(a)
print(A.__bases__)
print(A.__mro__)
#解析循序为A->B->B1->B2->C->OBJECT

5. 多态:统一接口,不同实现

多态允许不同类的对象通过相同的接口进行调用,根据对象的实际类型执行不同的实现。简单来说,就是 "一个接口,多种实现"。


class Animal:
    def speak(self):
        raise NotImplementedError("子类必须重写speak")

# a1 = Animal()
# a1.speak()


class Dog(Animal):
    def speak(self):
        print("汪汪汪")

class Cat(Animal):
    pass
    def speak(self):
        print("喵喵喵")


d1 = Dog()
d1.speak()

c1 = Cat()
c1.speak()

6、公有、私有与保护属性

1,公有属性与方法

在 Python 类中,默认情况下,定义的属性和方法都是公有的。这意味着在类的外部,可以直接通过对象访问这些属性和调用这些方法。

2,私有属性与方法

在 Python 中,通过在属性或方法名前添加双下划线(__)来定义私有属性和方法。私有属性和方法在类的外部是不能直接访问的,它们主要用于类的内部实现,对外部隐藏实现细节。

3, 保护属性

在属性或方法名前添加单下划线(_)表示保护属性或方法。从约定俗成的角度来看,它暗示着该属性或方法是 “受保护的”,意味着子类可以访问,但不建议在类的外部直接访问。不过,在 Python 中,单下划线的属性和方法在语法上仍然是可以从外部访问的。

代码案例:

class Person:
    def __init__(self, name, age, sex):
        self.name = name        # 公有属性:姓名
        self.__age = age        # 私有属性:年龄(外部无法直接访问)
        self._sex = sex         # 保护属性:性别(约定不直接访问)

    def show(self):
        return f"名字: {self.name} 年纪:{self.__age}  性别:{self._sex}"

    def __str__(self):
        return self.show()

    def __get_age(self):
        #私有方法:打印并返回对象的年龄
        print(self.__age)
        return self.__age

    def pr(self):
        #公有方法:通过内部调用私有方法获取年龄
        self.__get_age()

# 测试Person类
# p1 = Person("dd",23,"男")
# print(p1.__age)  # 报错:无法直接访问私有属性
# print(p1,p1._sex)  # 可以访问保护属性
# print(p1.__get_age)  # 报错:无法直接访问私有方法
# p1.pr()  # 可以通过公有方法间接调用私有方法

class SuperPerson(Person):
    def __init__(self, name, age, sex, skill):
        super().__init__(name, age, sex)  # 调用父类构造函数
        self._skill = skill               # 保护属性:特殊技能

    def __str__(self):
        return f"{self.show()}  技能:{self._skill}"

s1 = SuperPerson("aaa", 22, "保密", "喷火")
print(s1)           # 调用重写的__str__方法
print(s1._skill)    # 可以直接访问保护属性
print(s1._sex)      # 可以直接访问父类的保护属性

7.类的成员

1、类属性:类与实例的 “共享与独立”

类属性是定义在类中、方法外的属性,它属于类本身,所有该类的实例共享这一属性。就像代码里 Person 类的 info = "两脚兽" ,它是类层面的公共数据

class Person:
    info = "两脚兽"  # 类属性

访问与修改:

类访问:直接用 类名.属性名 ,如 Person.info ,获取的是类属性原始值,修改也会直接改变类属性(所有实例后续访问会受影响 )。

实例访问:实例默认会先找自己的实例属性,若没有,就会去类属性里找。像 p1.info ,初始时会拿到 Person.info 的值 。但如果对实例的该属性赋值(如 p1.info = "666" ),就会在实例上创建独立的实例属性,后续实例访问的是自己的,不再关联类属性。

p1 = Person("aaaa")
print(Person.info)  # 输出: 两脚兽,类直接访问类属性
print(p1.info)      # 输出: 两脚兽,实例访问类属性(未创建实例属性时)
p1.info = "666"     # 给实例p1创建独立的实例属性info
print(p1.info)      # 输出: 666,访问实例自己的属性
p2 = Person("李四")
print(p2.info)      # 输出: 两脚兽,p2未创建实例属性,访问类属性 

2、类方法:操作类本身的工具

@classmethod 装饰器

类方法用 @classmethod 装饰,第一个参数是 cls(代表类本身 ),而非实例方法的 self(代表实例 )。它主要用于操作类相关的逻辑,比如访问类属性、创建类的实例等。

@classmethod
def get_class(cls):
    return cls  # 返回类本身

@classmethod
def get_class_name(cls):
    print(cls.info)       # 访问类属性
    print(cls.__bases__)  # 查看类的父类元组
    print(cls.__mro__)    # 查看方法解析顺序
    return cls.__name__   # 返回类名 

3、静态方法:独立功能

静态方法特点

Util 类里的方法用 @staticmethod 装饰,它不需要 self 或 cls 参数,就是单纯的函数,逻辑上属于该类的工具功能,但不依赖类或实例的状态。

class Util:
    @staticmethod
    def max(a, b):  # 静态方法,实现两数比较取大
        return a if a > b else b

    @staticmethod
    def sum(a, b):  # 静态方法,实现两数求和
        return a + b 

8、动态添加类成员

1、动态添加实例属性与方法

1. 动态添加实例属性

在 Python 中,我们可以随时为对象添加新的属性,即使类定义中并未声明。这种特性使得对象的状态可以根据运行时需求灵活调整。

class Person:
    """空的Person类"""

p1 = Person()
p2 = Person()

# 为p1动态添加name属性
p1.name = "小明"
print(p1.name)  # 输出: 小明

# p2没有name属性,以下代码会报错
# print(p2.name)  

2. 动态添加实例方法

动态添加方法相对复杂一些,有两种主要方式:

方式一:直接绑定函数(需手动传递 self)

这种方式简单直接,但调用时需要手动传递实例对象作为第一个参数。

def get_name0(self):
    return self.name

def set_name0(self, name):
    self.name = name
    return self.name

# 直接将函数绑定到对象
p1.get_name = get_name0
p1.set_name = set_name0

# 调用时需手动传递self参数
print(p1.get_name(p1))  # 输出: 小明
print(p1.set_name(p1, "小红"))  # 输出: 小红

方式二:使用 types.MethodType(自动传递 self)

这是更优雅的方式,使用types.MethodType将函数绑定到实例,调用时会自动传递实例对象。

import types

# 将函数转换为绑定方法
p1.get_name = types.MethodType(get_name0, p1)
p1.set_name = types.MethodType(set_name0, p1)

# 正常调用,无需手动传递self
print(p1.get_name())  # 输出: 小红
print(p1.set_name("小李"))  # 输出: 小李

2、动态添加类属性与方法

1. 动态添加类属性

类属性属于类本身,可以被所有实例共享。动态添加类属性后,所有实例都能访问到这个新属性。

# 动态添加类属性
Person.info = "两脚兽"

# 类和实例都能访问类属性
print(Person.info)  # 输出: 两脚兽
print(p1.info)      # 输出: 两脚兽
print(p2.info)      # 输出: 两脚兽

# 注意:如果实例有同名属性,会优先访问实例属性
p1.info = "特殊人类"
print(p1.info)      # 输出: 特殊人类(实例属性)
print(p2.info)      # 输出: 两脚兽(类属性)

2. 动态添加类方法

类方法通过@classmethod装饰器定义,第一个参数是类本身(通常用cls表示)。动态添加类方法时,直接将函数赋值给类即可。

@classmethod
def get_class0(cls):
    return cls

@classmethod
def get_bases0(cls):
    return cls.__bases__

@classmethod
def get_doc0(cls):
    return cls.__doc__

# 动态添加类方法
Person.get_class = get_class0
Person.get_bases = get_bases0
Person.get_doc = get_doc0

# 调用类方法
print(Person.get_class() == Person)  # 输出: True
print(Person.get_bases())            # 输出: (<class 'object'>,)
print(Person.get_doc())              # 输出: 空的Person类

3. 动态添加静态方法

静态方法通过@staticmethod装饰器定义,不依赖于类或实例。动态添加静态方法的方式与类方法类似。

@staticmethod
def say_hello():
    return "Hello, World!"

@staticmethod
def add(a, b):
    return a + b

# 动态添加静态方法
Person.say_hello = say_hello
Person.add = add

# 调用静态方法
print(Person.say_hello())  # 输出: Hello, World!
print(Person.add(3, 5))    # 输出: 8

# 也可以通过实例调用静态方法
print(p1.say_hello())      # 输出: Hello, World!

9.Python 中的属性操作

hasattr:判断对象是否有指定属性​

hasattr 函数的作用是判断一个对象是否拥有指定名称的属性。它的语法非常简单:hasattr(object, name)。其中,object 是要检查的对象,name 是属性的名称,需要以字符串的形式传入。​

当对象拥有该属性时,函数返回 True;反之,则返回 False。举个例子,我们定义一个简单的类:

class Student:
    def __init__(self, name):
        self.name = name

s = Student("小明")
print(hasattr(s, "name"))  # 输出 True,因为 s 有 name 属性
print(hasattr(s, "age"))   # 输出 False,因为 s 没有 age 属性

setattr:为对象设置属性​

setattr 函数用于为对象设置属性。如果该属性不存在,就会新建这个属性;如果属性已存在,就会修改属性的值。它的语法是:setattr(object, name, value)。这里的 object 是要设置属性的对象,name 是属性名(字符串形式),value 是要设置的属性值。​

还是用上面的 Student 类举例:

s = Student("小明")
setattr(s, "age", 18)  # 为 s 设置 age 属性,值为 18
print(s.age)  # 输出 18
setattr(s, "name", "小红")  # 修改 name 属性的值
print(s.name)  # 输出 小红

getattr:获取对象的属性值​

getattr 函数的功能是获取对象指定属性的值。它的语法为:getattr(object, name[, default])。object 是目标对象,name 是属性名(字符串),default 是可选参数,当属性不存在时,会返回这个默认值;如果没有设置默认值且属性不存在,就会抛出 AttributeError 异常。​

例如:

s = Student("小明")
setattr(s, "age", 18)
print(getattr(s, "name"))  # 输出 小明
print(getattr(s, "age"))   # 输出 18
print(getattr(s, "gender", "未知"))  # 输出 未知,因为 gender 属性不存在且设置了默认值

delattr:删除对象的属性​

delattr 函数用于删除对象的指定属性,其语法为:delattr(object, name)。object 是要操作的对象,name 是要删除的属性名(字符串)。如果属性不存在,会抛出 AttributeError 异常。​

示例如下:

s = Student("小明")
setattr(s, "age", 18)
delattr(s, "age")  # 删除 age 属性
print(hasattr(s, "age"))  # 输出 False,说明 age 属性已被删除

10.property:属性封装

 1. 使用装饰器语法

class Person:
    def __init__(self, name):
        self.__name = name

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

    @name.setter
    def name(self, name):
        self.__name = name

这种方式更加简洁,是 Python 中推荐的写法。@property 装饰器将方法转换为只读属性,而 @<property>.setter 装饰器则为该属性添加了 setter 方法。

2. 使用 property 类

对于 age 和 sex 属性,我们使用了 property 类的构造函数:

class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age


    def __get_age(self):
        return self.__age

    def __set_age(self, value):
        self.__age = value

    age = property(__get_age, __set_age)

3.property 的优势

  1. 接口一致性:保持了属性访问的简洁性,与直接访问普通属性的语法完全一致,无需调用方法。

  2. 代码可维护性:将属性的访问逻辑封装在类内部,当需求变化时,只需修改类的实现,而不会影响外部调用代码。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值