Python高级语法(面向对象)

Python 的面向对象编程(OOP)是一种基于类和对象的编程范式,支持封装、继承、多态等特性。

1. 初识类和对象

示意图:

       /-------> 陶吉吉 小镇姑娘,二十二
歌手(类)  
       \-------> 周杰伦 花海,游园会
    
       /-------> 国漫 狐妖小红娘 牧神记
动漫(类)  
       \-------> 日漫 咒术回站 鬼灭之刃
​
       /-------> 100 (对象)
int(类)  
       \-------> 200 (对象)

1.1 类→Class

物以类聚

1.1.1 概念

类是面向对象编程(OOP)的核心概念之一。它是一种用户自定义的数据类型,封装了数据和操作数据的方法。类可以看作是具有相同属性和方法的对象的集合。

1.1.2 创建

数据成员:表明事物的特征。 相当于变量

方法成员:表明事物的功能。 相当于函数

通过class关键字定义类。

类的创建语句语法:

class 类名 (继承列表):
    实例属性(类内的变量) 定义
    实例方法(类内的函数method) 定义
    类变量(class variable) 定义
    类方法(@classmethod) 定义
    静态方法(@staticmethod) 定义

参考代码:

类名就是标识符,建议(有点强烈首字母大写

类名实质上就是变量,它绑定一个类

self代表类实例化的对象本身,不一定是self,这是一个形参,可以自己命名

1.2 对象→Object

1.2.1 概念

对象类的实例化,是类的实际数据存储,具有类所定义的属性和方法

1.2.2 创建

构造函数调用表达式

变量 = 类名([参数])

说明

变量存储的是实例化后的对象地址

类参数按照初始化方法的形参传递

对象是类的实例,具有类定义的属性和方法。

通过调用类的构造函数来创建对象。

每个对象有自己的状态,但共享方法。

示例代码


创建了两个对象,dog1和dog2,它们的id不同,所以同一个类申明的变量,不是同一个,一个类可以申明多个对象,包含了这个对象的变量和函数,互不干扰。

self.name = name 操作是,对Person这个对象创建name这个变量(左边),把name对象赋值给左边的name,age同理。

2. 属性和方法

类的属性和方法是类的核心组成部分,它们用于定义类的状态和行为。

2.1 实例属性

每个实例有自己的变量,称为实例变量(也叫属性)。实例属性是每个对象独有的属性,定义时需要使用self关键字。实例属性在__init__方法中初始化。

属性的使用语法

实例.属性名

属性使用

2.2 实例方法

class 类名(继承列表):
    def 实例方法名(self, 参数1, 参数2, ...):
        "文档字符串"
        语句块

实例方法就是函数,至少有一个指向实例对象的形参self

调用

实例.实例方法名(调用传参)
# 或
类名.实例方法名(实例, 调用传参)

带有实例方法的简单的Dog类


实列方法,需要有一个指针指向实列对象的形参self,这个self可以自己命名。在实列对象创建的变量在调用的时候,不需要用self来调用变量,如果是这个类自带的变量,在类下面的定义的函数中调用的时候,就需要用self。

2.3 类属性

类属性是类本身具有的属性,属于所有实例对象。定义时直接在类中定义,不依赖于self

作用:

通常来存储共有的属性

类属性说明

类属性,可以通过该类直接访问

类属性,可以通过类的实例直接访问

类属性示例

2.4 类方法

类方法是用于描述类的行为的方法,类方法属于类,不属于该类创建的对象

说明

类方法需要使用@classmethod装饰器定义

类方法至少有一个形参用于绑定类,约定为 cls

类和该类的实例都可以调用类方法

类方法不能访问此类创建的对象的实例属性

类方法示例1

调用A.get_v的函数,来得到v的值v是A类里面的变量,所以要用一个形参变量cls(规范命名,也可以不是这个),输出0;调用A.set_v函数,将传入的参数100,传递给cls.v这个A类的v,再调用get_v函数,输出的就是100;因为A类里面的v已经改变了,所以申明的对象a里面的v也是100.

类方法示例2


类可以调用类方法,实例可以调用实列方法,实例可以调用类方法,类方法不能直接调用实例方法,如果需要调用就得添加实例对象

可以通过类函数访问类里面的变量,可以通过实例化的对象来访问类里的变量,可以通过类里面的方法来调用其他的类方法

2.5 静态方法

静态方法是Python中的一种特殊方法,它不需要访问类的实例或类本身的状态,因此不需要selfcls参数。静态方法通常用于将一些与类相关的辅助功能组织在类中,使代码结构更清晰。

使用@staticmethod装饰器定义

通过类或类实例调用

可以访问类属性不能访问实例属性

静态方法示例

因为在静态函数里面,没有传入self或者slc这样的形参,所以就没办法去访问实例或者类,只能是去访问变量参数

静态方法的特点

  静态方法不需要实例化类就可以直接调用。

  静态方法不依赖于类或实例的状态,因此无法访问selfcls

  静态方法可以被子类继承,但不会根据子类的类属性或实例属性进行修改。

静态方法的使用场景

  当一个方法与类相关,但不需要访问类或实例的属性时,可以将其定义为静态方法。

  将一些工具函数或辅助函数组织在类中,便于代码管理。

静态方法与类方法、实例方法的区别

  实例方法:需要实例化类后调用,第一个参数是self,表示类的实例。

  类方法:使用@classmethod装饰器定义,第一个参数是cls,表示类本身。

  静态方法:不需要实例化类即可调用,没有selfcls参数。

2.6 构造方法和初始化方法


__new__ 方法

作用:负责对象的创建和内存分配。它在对象实例化时被调用,返回一个新的对象实例。

参数:第一个参数是类本身(cls), followed by the arguments passed to the class instantiation.

调用:通常不需要显式定义__new__,因为Python会自动调用基类object__new__方法。如果你重写了__new__,必须确保返回一个实例(通常是通过调用super().__new__(cls))。

注意:如果你重写了__new__,它必须返回一个实例,否则__init__不会被调用。


__init__ 方法

作用:负责初始化已经创建的对象。它在__new__之后被调用,用于设置对象的初始状态。

参数:第一个参数是实例本身(self), followed by other parameters used to initialize the object.

调用:在对象创建后自动调用,用于初始化对象的属性。


实际开发中的使用

通常不需要显式定义__new__:在大多数情况下,你只需要定义__init__来初始化对象。

单例模式__new__ 在实现单例模式时非常有用,因为它可以控制对象的创建过程。

自定义对象创建行为:如果你需要在对象创建时添加一些自定义逻辑(如缓存、对象池等),可以重写__new__

2.8 魔术方法

魔术方法是 Python 中一种特殊的方法,它们允许咱们自定义类的行为,以便与内置 Python 功能(如运算符、迭代、字符串表示等)交互。魔术方法以双下划线(__)开头和结尾,例如 __init____str____add__ 等。

2.8.1 常用方法

__init__(self, ...): 初始化对象,通常用于设置对象的属性。

__str__(self): 定义对象的字符串表示形式,可通过str(object)或print(object)调用。例如,您可以返回一个字符串,描述对象的属性。

__repr__(self): 定义对象的“官方”字符串表示形式,通常用于调试。可通过repr(object)调用。

__len__(self): 定义对象的长度,可通过len(object)调用。通常在自定义容器类中使用。

__getitem__(self, key): 定义对象的索引操作,使对象可被像列表或字典一样索引。例如,object[key]。

__setitem__(self, key, value): 定义对象的赋值操作,使对象可像列表或字典一样赋值。例如,object[key] = value。

__delitem__(self, key): 定义对象的删除操作,使对象可像列表或字典一样删除元素。例如,del object[key]。

__iter__(self): 定义迭代器,使对象可迭代,可用于for循环。

__next__(self): 定义迭代器的下一个元素,通常与__iter__一起使用。

__add__(self, other): 定义对象相加的行为,使对象可以使用+运算符相加。例如,object1 + object2。

__sub__(self, other): 定义对象相减的行为,使对象可以使用-运算符相减。

__eq__(self, other): 定义对象相等性的行为,使对象可以使用==运算符比较。

__lt__(self, other): 定义对象小于其他对象的行为,使对象可以使用<运算符比较。

__gt__(self, other): 定义对象大于其他对象的行为,使对象可以使用>运算符比较。

__call__(self, other) 是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。

2.8.2 案例参考

__init__(self, ...):初始化对象

用于设置对象的属性,当对象被创建时自动调用。

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


__str__(self):定义对象的字符串表示形式

通过 str(object)print(object) 调用,返回一个易读的字符串。class Person:
    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"


__repr__(self):定义对象的“官方”字符串表示形式

通常用于调试,返回一个更详细的字符串。

class Person:
    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"


__len__(self):定义对象的长度

通过 len(object) 调用,通常在自定义容器类中使用。

class MyList:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)


__getitem__(self, key):定义对象的索引操作

使对象可被像列表或字典一样索引。

class MyList:
    def __getitem__(self, key):
        return self.items[key]


__setitem__(self, key, value):定义对象的赋值操作

使对象可像列表或字典一样赋值。

class MyList:
    def __setitem__(self, key, value):
        self.items[key] = value


__delitem__(self, key):定义对象的删除操作

使对象可像列表或字典一样删除元素。

class MyList:
    def __delitem__(self, key):
        del self.items[key]


__add__(self, other):定义对象相加的行为

使对象可以使用 + 运算符相加。

class Vector:
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)


__sub__(self, other):定义对象相减的行为

使对象可以使用 - 运算符相减。

class Vector:
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)


__eq__(self, other):定义对象相等性的行为

使对象可以使用 == 运算符比较。

class Person:
    def __eq__(self, other):
        return self.name == other.name and self.age == other.age


__lt__(self, other):定义对象小于其他对象的行为

使对象可以使用 < 运算符比较。

class Person:
    def __lt__(self, other):
        return self.age < other.age


__gt__(self, other):定义对象大于其他对象的行为

使对象可以使用 > 运算符比较。

class Person:
    def __gt__(self, other):
        return self.age > other.age


__call__(self, ...):定义对象被调用的行为

使对象可以像函数一样被调用。

class CallableClass:
    def __call__(self, value):
        return value * 2


3. OOP基本特性

OOP的四大基本特性是封装继承多态抽象

3.1 封装

封装是面向对象编程的核心概念之一,它将数据(属性)和操作数据的方法封装到一个类中,限制对内部实现细节的访问,只暴露必要的接口给外部使用。封装的主要目的是提高代码的安全性和可维护性。

实现封装的方式

在 Python 中,封装通过以下方式实现:

  1. 私有属性:在类中,通过在属性名前添加双下划线__)将其定义为私有属性,外部无法直接访问。

  2. 公有方法:提供公有方法(不以下划线开头)来操作私有属性,实现对内部数据的封装。


添加了(__),在类里面就是私有属性,如果直接通过实例来调用,会显示不存在,如果要来调用这个私有的变量,需要用函数来调用

3.2 继承/派生

儿子继承了父亲,父亲派生了儿子~

Python中所有的类最终都继承自内置的object类。

3.2.1 基础概念

继承/派生

        继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的能力。

        派生类就是从一个已有类中衍生出新类,在新的类上可以添加新的属性和行为

继承/派生的作用

        用继承派生机制,可以将一些共有功能加在基类中。实现代码的共享。

        在不改变基类的代码的基础上改变原有类的功能

继承/派生名词:

        基类(base class)/超类(super class)/父类(father class)

        派生类(derived class)/子类(child class) 

3.2.2 继承的实现

继承语法:

Python 支持多继承,即一个子类可以继承多个父类。

class 子类名(父类名1,父类名2,父类名3):
    # 子类的内容

3.2.3 覆盖

子类中实现与基类同名的方法,我们叫覆盖

作用:

实现和父类同名,但功能不同的方法

覆盖示例

3.2.4 课堂练习
写一个类Bicycle类, 有run方法,调用时显示骑行里程km
    class Bicycle:
        def run(self, km):
            print("自行车骑行了", km, "公里")
​
再写一个类EBicycle,在Bicycle类的基础上,添加电池电量volume属性,有两个方法:
  1. fill_charge(vol) 用来充电, vol 为电量
  2. run(km)方法每骑行10km消耗电量1度,同时显示当前电量,当电量耗尽则,则调用Bicycle的run方法
class EBicyle(Bicycle):
    ...

参考:

class Bicycle:
    def run(self, km):
        print("自行车骑行了", km, "公里")
​
class EBicycle(Bicycle):
    def __init__(self, vol):
        self.cur_volume = vol  # 当前电量
    def run(self, km):
        e_km = min(km, self.cur_volume * 10)  # 求km和 乘余电量能行走的最小里程
        self.cur_volume -= e_km / 10
        if e_km > 0:
            print("电动车骑行了 %d km" % e_km,
                  "剩余电量:", self.cur_volume)
        if km > e_km:
            super().run(km - e_km)
    def fill_charge(self, vol):
        print("电动自行车充电", vol, "度")
        self.cur_volume += vol
​
b = EBicycle(5)  # 新买的电动车内有5度电
b.run(10)  # 电动骑行了10km 还剩 4度电
b.run(100)  # 电动骑行了 40 km ,还剩 0 度电, 用脚登骑行了60km
b.fill_charge(10)  # 电动自行车充电 10 度
b.run(50)  # 骑行了50公里剩余 5度电

3.3 多态

多态是面向对象编程的核心概念之一,它允许不同类的对象对同一消息作出不同的响应。在 Python 中,多态是通过鸭子类型(Duck Typing)来实现的,即只要一个对象具有某种方法或属性,就可以被当作是具有该方法或属性的对象来使用,而不需要关心它具体属于哪个类。

多态的实现方式

在 Python 中,多态可以通过以下几种方式实现:

3.3.1 方法重写

在继承关系中,子类可以重写父类的方法,以实现自己的特定功能。当通过父类类型的引用调用方法时,会根据实际对象的类型调用相应的方法。

在这个例子中,Animal 类定义了一个 speak 方法,但没有实现具体的功能。DogCat 类分别继承了 Animal 类,并重写了 speak 方法,实现了自己的功能。当通过 dogcat 对象调用 speak 方法时,会根据实际对象的类型调用相应的方法。

3.3.2 接口一致性

只要不同的类具有相同的方法或属性,就可以通过统一的接口来调用它们,而不需要关心它们的具体类型。

在这个例子中,Shape 类定义了一个 area 方法,但没有实现具体的功能。CircleRectangle 类分别继承了 Shape 类,并实现了 area 方法。通过统一的 area 接口,可以调用不同形状的面积计算方法。

3.4 重写

通过方法重写,子类可以实现与父类不同的功能,从而实现多态。

3.4.1 对象转字符串重写

在 Python 中,可以通过重写 __str____repr__ 方法来自定义对象转换为字符串时的行为。

__str__ 方法

__str__ 方法用于返回对象的字符串表示,当使用 str() 函数或 print() 函数时会被调用。如果没有定义 __str__ 方法,则会使用 __repr__ 方法的结果。


__repr__ 方法

__repr__ 方法返回对象的官方字符串表示,通常用于调试和开发。它应该返回一个可以用来重新创建该对象的字符串。

class MyNumber:
    """此类用于定义一个自定义的类,用于演示str/repr函数重写"""
​
    def __init__(self, value):
        """构造函数,初始化MyNumber对象"""
        self.data = value
​
    def __str__(self):
        """转换为普通字符串"""
        return "%s" % self.data
​
​
n1 = MyNumber("一只猫")
n2 = MyNumber("一只狗")
print("str(n2) ===>", str(n2))
3.4.2 内建函数重写

__abs__ abs(obj) 函数调用

__len__ len(obj) 函数调用

__reversed__ reversed(obj) 函数调用

__round__ round(obj) 函数调用

内建函数 重写示例

3.4.2 运算符重载

运算符重载是指让自定义的类生成的对象(实例)能够使用运算符进行操作

运算符重载的作用

让自定义类的实例像内建对象一样进行运算符操作

让程序简洁易读

对自定义对象将运算符赋予新的运算规则

运算符重载说明:

运算符重载方法的参数已经有固定的含义,不建议改变原有的意义

方法名运算符和表达式说明
__add__(self, rhs)self + rhs加法
__sub__(self, rhs)self - rhs减法
__mul__(self, rhs)self * rhs乘法
__truediv__(self, rhs)self / rhs除法
__floordiv__(self, rhs)self // rhs地板除
__mod__(self, rhs)self % rhs取模(求余)
__pow__(self, rhs)self ** rhs
rhs (right hand side) 右手边

二元运算符重载方法格式:

def __xxx__(self, other):
    ....

算术运算符重载示例


4. super函数

在 Python 中,super() 是一个内置函数,用于调用父类的方法。它在多继承和方法重写的情况下非常有用,可以帮助我们实现对父类方法的扩展和重用。

4.1 基本使用

在子类方法中使用 super().add() 调用父类中已被覆盖的方法

使用 super(Child, obj).myMethod() 用于子类对象调用父类已被覆盖的方法

Box2声明一个对象b2,对b2.x变量赋值400,调用super(Box2,b2).fn()函数,意思是我要调用Box2声明的b2对象,用b2对象来调用基类的fn函数,所以把b2传给了fn函数,所以self.x=400。

4.2 super().__init__()

通过 super().\_\_init\_\_() 调用父类构造函数,以确保父类的构造函数被正确调用和初始化。

当我用Box2声明了一个对象b2,需要对b2对象进行传参,在__init__的时候,不能忘了对父类也进行__init__,super(Box2,self).__init__(width,height),意思是我要将用self,也就是b2来调用父类的init函数,传递给的形参也是b2的

为什么使用 super().__init__()

代码重用避免在子类中重复父类的初始化代码。

正确初始化:确保父类的初始化逻辑(如设置属性、分配资源等)被执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值