Python3 | 练气期,面向对象、类魔术方法,类修饰器!

2024-07-29 10:02:36 浏览数 (3)

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ]

0x00 前言简述

如果你接触过 Java、Golang 编程语言,那么你一定知道面向对象编程(OOP)的概念。面向对象编程(OOP)是相对于面向过程编程而言的,面向过程编程是一种以过程为中心的开发模式,而面向对象编程则是以对象为中心的开发模式。

本章节我们将详细介绍Python的面向对象编程,不过在此之前我们先简单了解一下面向对象技术相关概念。

  • 类[Class]: 描述具有相同的属性和方法的对象的集合, 其中对象是类的实例。
  • 方法:类中定义的函数。
  • 属性:类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 类变量:类变量在整个实例化的对象中是公用的,类变量定义在类中且在函数体之外,注:类变量通常不作为实例变量使用
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • 封装: 对外部隐藏对象的工作细节,例如,采用 get_name() 方法来获取对象的名字, set_name() 方法来设置对象的名字。
  • 继承(Inheritance):即子类继承父类的属性和方法,可以重用父类的代码,继承也允许把一个派生类(derived class)的对象作为一个基类对象对待。例如,Dog 类的对象派生自 Animal类。
  • 方法重写(Method Overriding):如果从父类继承的方法不能满足子类的需求,可以对其进行改写,此过程叫方法的覆盖override)也称为重写
  • 多态(Polymorphism): 允许不同类的对象可以通过相同的接口调用方法,从而实现相同的操作。例如,在 Dog类和Cat类中eat()方法,输出可以是不同的信息。
  • 重用(Reusability):指的是通过类的继承、组合和模块化等技术,使得代码可以被多个不同的程序或不同的部分重复使用,从而减少代码重复,提升开发效率和代码的可维护性。
  • 实例化:创建一个类的实例,类的具体对象。
  • 对象(Object):通过类定义的数据结构实例,对象包括两个属性(数据成员类变量和实例变量)和方法(由函数构成)。

weiyigeek.top-面向对象编程中的概念图

温馨提示:作者后续实践主要在 Ubuntu 24.04 TLS Python 3.12 Jupyter Notebook 环境中运行,若要配置为作者的学习环境,可参考《#AIGC学习之路》专栏中的流程,此外便于看友一起学习Python系列笔记,访问《#Python学习之路》专栏从前往后按照时间进行学习。

温馨提示:若各位看友在其他平台看到此篇文章,一定要关注公众号【全栈工程师修炼指南】进行持续学习!我们一同学习,一起进步,关注后回复【加群】哟!


0x01 Python 面向对象编程

描述: Python 本身就是一门面向对象编程语言(无处不对象),它支持面向对象的程序设计方法, 在Python中的类提供了面向对象编程的所有基本功能,例如:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法

1.类和对象

Python 中一切皆为对象,类和对象是面向对象编程的基石,类对象支持两种操作属性引用和实例化。

类和对象是什么关系呢?

类和对象的关系就如同模具和用这个模具制作出的物品之间的关系, 一个类为它的全部对象给出了一个统一的定义, 而他的每个对象则是符合这种定义的一个实体,因此类和对象的关系就是抽象和具体的关系;

其基础语法如下:

代码语言:javascript复制
# 定义类,类名首字母大写,分隔单词首字母也是大写
class ClassName:
    <statement-1>
    ......
    <statement-N>
    # 属性(类变量)
    classVar = 1024
 
    # 构造方法
    def __init__(self, arguments...):
      # 实例变量
      self.data = arguments[0]
  
    # 自定方法,采用驼峰命名法
    def customMethod(self):
      print(self.data)
      pass

# 实例化类,即类对象
# 类对象创建后,类命名空间中所有的命名都是有效属性名。
object = ClassName([arguments...])

# 访问类中的属性
object.attribute

# 访问类中的方法
object.customMethod()

类中特殊属性

  • __private_attrs 属性:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问,例如:内部调用 self.__private_attrs
  • __doc__ 属性:类文档字符串
  • __class__ 属性:类名
  • __bases__ 属性:返回基类元组,可以在运行时修改这个属性来实现动态继承。

类中特殊方法

  • __private_method(): 定义私有方法,只能在类的内部调用,例如:self.__private_methods()
  • __init__():构造函数,在生成对象时调用
  • __iter__(): 定义迭代容器类型数据时必须实现的方法(在迭代器与生成器文章中讲解过)
  • __next__(): 定义迭代容器类型数据时必须实现的方法(在迭代器与生成器文章中讲解过)
  • __del__():析构函数,释放对象时使用

注:类中 self 参数的作用是绑定方法(代表的是类的实例),有了它我们就可以在类的内部进行调用类中属性和方法了,所以类中方法中第一个参数名称,通常设置为 self, 也可以使用 this,但是最好还是按照约定使用 self。。

简单示例

  • 一个简单的类与对象定义,实例化示例
代码语言:javascript复制
# 定义类
class MyClassTest:
    """
    MyClassTest 类:简单的类与对象定义,实例化示例
    """
    count = 0               # 静态属性
    public_attrs = 1024     # 公共属性
    __private_attrs = 2048     # 私有属性

    def __init__(self, name, url):
        MyClassTest.count = MyClassTest.count   1  # 类名.属性名的形式引用 (每实例化一次 1)
        self.name = name       # 实例变量
        self.url = url         # 实例变量

    def customPrint(self):
        print('公共 customPrint 方法')  # 公共方法
        print("当前对象的地址: ",self)
        print("当前类的名称: ",self.__class__)
        print( self.name, self.url)
        print('公共 customPrint 方 -> 调用私有 __privatePrint 方法')  # 公共方法
        self.__privatePrint()         #  

    def __privatePrint(self):          # 私有方法
        print('私有 __privatePrint 方法')
    
    def getCount(self):   
        return MyClassTest.count   #类的静态属性应用(计算该类被实例化的次数)

# 实例化类,生成类对象
obj = MyClassTest("全栈工程师修炼指南","weiyigeek.top")
obj1 = MyClassTest("全栈工程师修炼指南","weiyigeek.top")

# 获取类中的属性
print("obj.__doc__ : ",obj.__doc__)
print("obj.__class__ : ",obj.__class__)
print("obj.public_attrs: ",obj.public_attrs)

# 调用类中的方法
obj.customPrint()
count = obj.getCount()
print('类 {},实例化 {} 次'.format(obj.__class__,count))

# 使用如下方法可以直接访问类中私有属性和方法(特别注意)
print("外部获取私有 _private_attrs 变量 = ", obj._MyClassTest__private_attrs)          # 外部直接调用私有属性
obj._MyClassTest__privatePrint()          # 外部直接调用私有方法

执行结果:

代码语言:javascript复制
# 1.获取类中的属性
obj.__doc__ :  
    MyClassTest 类:简单的类与对象定义,实例化示例
    
obj.__class__ :  <class '__main__.MyClassTest'>
obj.public_attrs:  1024

# 2.调用类中的方法
公共 customPrint 方法
当前对象的地址:  <__main__.MyClassTest object at 0x766f5f51ae10>
当前类的名称:  <class '__main__.MyClassTest'>
全栈工程师修炼指南 weiyigeek.top
公共 customPrint 方 -> 调用私有 __privatePrint 方法
私有 __privatePrint 方法
类 <class '__main__.MyClassTest'>,实例化 2 次
外部获取私有 _private_attrs 变量=  2048
私有 __privatePrint 方法

特别注意

  • 定义类时,其类名首字母大写,如果类名中包含下划线,则下划线左边的字符大写,如:_ClassName
  • 定义类中方法时,其方法名遵守驼峰命名法,如果方法名中包含下划线,则下划线左边的字符大写,如:_methodName
  • python 采用一种叫“name mangling”技术, 将以双下划线开头的变量名巧妙的改了个名字而已, 所以在外部调用类中的私有属性或方法时,可使用 obj._MyClassTest__privatePrint()进行调用但在开发中通常不建议这样使用,因为私有属性或方法通常是为了保护内部实现细节,外部直接调用会破坏封装性。

2.类封装

在 Python 中,封装是面向对象编程(OOP)的一个基本概念,它允许我们将数据(属性)和行为(方法)绑定在一起,并限制对某些组件的直接访问。

使用封装的主要好处是,有助于将对象的内部状态与外部接口隔离开来,增强代码的安全性、可维护性和灵活性。在实际开发中,善于利用封装可以显著提高代码质量。

示例演示:

  • 示例1.定义一个类,简单演示类的封装特性
代码语言:javascript复制
#!/usr/bin/python3
# -*- coding: UTF-8 -*-

class MyClass:
    name = '程序员'      # 公共属性
    msg = 'I Love Python'
    __score = 0          # 私有属性,类外部无法直接进行访问
    
    def __init__(self,name,msg,score):  # 构造函数
        self.name = name
        self.msg = msg
        self.__score = score
    
    def get_name(self):        # 公共方法,获取name属性
        return self.__name

    def set_name(self, name):  # 公共方法,设置name属性
        self.__name = name

    def get_score(self):       # 公共方法,获取私有属性
        return self.__score 

    def set_score(self, score):   # 公共方法,设置私有属性
        if 0 < score < 120:
            self.__score = score

obj = MyClass("全栈工程师修炼指南","人生苦短,及时Python",256)   #实例化
print("姓名:",obj.get_name())           # 获取name属性
print("分数:",obj.get_score())          # 获取score属性
obj.set_score(120)                       # 设置score属性值
print("设置后的分数:",obj.get_score())  # 获取修改后的score属性

############ 执行结果 ##############
姓名: 全栈工程师修炼指南
分数: 256
设置后的分数: 64
  • 示例2.定义一个矩形类,获取面积、周长,主要演示类的封装特性。
代码语言:javascript复制
#!/usr/bin/python3
# -*- coding: UTF-8 -*-

class Rectangle:
  length = 5
  width = 6
  #设置长宽
  def setRect(self,length,width):
    self.length = length
    self.width = width

  #获取长宽
  def getRect(self):
    print("矩形的长宽为:%d , %d" %(self.length,self.width))

  #获取面积
  def getArea(self):
    print("矩形面积 =",self.width * self.length)

  #获取周长
  def getGirth(self):
    print("矩形周长 =",2 * (self.width   self.length))

rect = Rectangle()
rect.setRect(15,16)
rect.getRect()
rect.getArea()
rect.getGirth()

############ 执行结果 ##############
矩形的长宽为:15 , 16
矩形面积 = 240
矩形周长 = 62

特别注意:

  • 利用继承和组合机制来进行扩展时候,在类名/属性名(用名词)/方法名(用动词)的命名建议要规范化
  • 私有属性就是在属性或方法名字前边加上双下划线,从外部是无法直接访问到并会显示AttributeError错误
  • 当你把类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。
  • 类中属性名与方法名一定不要一致,否则属性会覆盖方法,导致BUG的发生;

3.类继承

描述: Python 同样支持类的继承,如果一种语言不支持继承,类就没有什么意义,使用继承可以很方便地复用父类的代码。

Q&A: 面向对象继承机制给好处是?

答:如果一个类 A 继承自另一个类 B,就把这个 A 称为 B 的子类,把 B 称为 A 的父类、基类或超类。 所以说,继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码(偷懒)。 在子类继承父类的同时, 可以重新定义某些属性, 并重写 overwrite 某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能,另外为子类追加新的属性和方法也是常见的做法。

在 Python 类继承中, 可以实现单继承、多重继承、动态继承几种方式,其中多重继承是 Python 中比较高级的特性。

  • 单继承:一个类只继承一个父类,简单且常见。
  • 多重继承:一个类可以继承多个父类,可以组合多个父类的功能,但继承关系复杂。
  • 动态继承:在运行时改变继承关系,适用于高级应用场景,例如插件系统或测试模拟,但需谨慎使用。

特别注意: 多重继承使用不当会导致重复调用(也叫钻石继承、菱形继承)的问题,导致代码执行效率低。

语法格式

代码语言:javascript复制
class 子类名(基类,父类,或者超类名称)
class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    ....
    <statement-N>

温馨提示: 圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

weiyigeek.top-类中继承的搜索

简单示例

  • 示例1.单继承是指一个类只继承一个父类,例如
代码语言:javascript复制
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# 父类
class Person:
    name = ''     # 基本属性
    __UID = 0     # 私有属性
    # 构造函数
    def __init__(self, *args):
        self.name = args[0]
        self.__UID = args[1]

    # 类方法
    def get_person_info(self):
        print("人员信息: %s , %d" %(self.name,self.__UID))

# 子类
class Student(Person):
    grade = ''
    def __init__(self, *args):
        # 调用父类的构造函数
        Person.__init__(self,args[0],args[1])
        self.grade = args[2]

    def get_student_info(self):
        print("姓名: %s , 年级: %s ,身份证号:%d  " %(self.name,self.grade,self._Person__UID))  

# 实例化类,生成对象
xs = Student('经天纬地',512301198010284307,'2018级')

# 调用父类的方法
xs.get_person_info()

# 调用自身类方法
xs.get_student_info()


# 执行结果:
人员信息: 经天纬地 , 512301198010284307
姓名: 经天纬地 , 年级: 2018级 ,身份证号:512301198010284307  
  • 示例2.多重继承是指一个类可以继承多个父类。这种方式可以让一个类具备多个父类的特性,但也可能导致复杂的继承关系,例如:
代码语言:javascript复制
#!/usr/bin/python3
# -*- coding: UTF-8 -*-

# 例如,类A,B分别表示不同的功能单元,C为A,B功能的组合,这样类C就拥有了类A, B的功能。
class A:
  nameA = "a"
  def __init__(self,*args):
    self.nameA = args[0]
    print("Class A", args[0])

  def get_a(self):
    print(self.nameA," -> ",self.__class__)

class B:
  nameB = "b"
  def __init__(self,*args):
    self.nameB = args[0]
    print("Class B", args[0])
      
  def get_b(self):
    print(self.nameB," -> ",self.__class__)


# 例子中,C 类同时继承了 A 和 B 类,并可以使用这两个父类中的方法。
class C(A, B): 
  name = ""
  def __init__(self,*args):
    # 调用 A 类构造函数
    A.__init__(self,args[0])
    # 调用 B 类构造函数
    B.__init__(self,args[1])
    self.name = args[2]
    print("Class C", args[2])
      
  def get_c(self):
    print(self.name," -> ",self.__class__)

# 实例化
c = C("父类 A","父类 B","类 C")

# 调用多重继承的方法
c.get_a()
c.get_b()
c.get_c()

############### 执行结果 ##############
Class A 父类 A
Class B 父类 B
Class C 类 C
父类 A  ->  <class '__main__.C'>
父类 B  ->  <class '__main__.C'>
类 C  ->  <class '__main__.C'>
  • 案例3:动态继承,即继承的基类是动态的(有时候是 A,有时候是 B),例如:
代码语言:javascript复制
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
class MyClass:
    def my_method(self):
        return "My Method"

class A:
    def method_a(self):
        return "A method"

class B:
    def method_b(self):
        return "B method"

class C(A):
    pass

# 方式1.修改类的 __bases__ 属性,来动态修改继承关系
C.__bases__ = (B,)

c = C()
print(c.method_b())  # 输出: B method


# 方式2.使用 type 函数动态创建类
def create_dynamic_class(base_class):
    return type('DynamicClass', (base_class,), {})

DynamicClass = create_dynamic_class(B)
d = DynamicClass()
print(d.method_b())  # 输出: B method

继承的另类实现:组合

什么是组合(组成)?

答:Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。 简单的说: 组合用于“有一个”的场景中,继承用于“是一个”的场景中

演示类的组合示例:

代码语言:javascript复制
#乌龟类
class Turtle:
    def __init__(self, x):
        self.num = x
        
# 鱼类
class Fish:
    def __init__(self, x):
        self.num = x

# 水池类
class Pool:
    def __init__(self, x, y):
        self.turtle = Turtle(x)    # 组合乌龟类进来 (关键点)
        self.fish = Fish(y)        # 组合鱼类进来 (关键点)
     
    def print_num(self):
        print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtle.num, self.fish.num))

# 实例化类
pool = Pool(1, 10)

# 调用方法
pool.print_num() # 结果: 水池里总共有乌龟 1 只,小鱼 10 条!

4.类重写

描述:在 Python 继承机制中, 子类可以重写父类的属性或方法, 来达到当父类方法功能不足时可自定义扩展的目的。

Python 严格要求方法需要有实例才能被调用, 这种限制其实就是Python所谓得绑定概念;

代码语言:javascript复制
#!/usr/bin/python3
# coding=utf-8
import random as r 

class Animal:
  def __init__(self):
      self.x = r.randint(0,10)
      self.y = r.randint(0,10)

  def eat(self):
      print("前往 Fishc 位置坐标({},{}), Shark 动物开始进食".format(self.x,self.y))


class Fish:
    def __init__(self):
        self.x = r.randint(0,10)
        self.y = r.randint(0,10)
    
    def move(self):
        self.x -= 1
        print("Fish 位置坐标是:",self.x,self.y)

class Shark(Animal,Fish):
    def __init__(self):
        super().__init__()        # 方法1:设置super() 指代 父类
        #Fish.__init__(self)      # 方法2: 调用父类构造方法
        self.hungry = True
   
    # 覆盖(重写) Animal 父类的 eat() 方法
    def eat(self):
        if self.hungry:
            print("饿了,要吃饭@!")
            self.hungry = False
        else:
            print("太撑了,吃不下了")

# 实例化类
obj = Shark()

# 调用父类方法
obj.move()
obj.eat()
# 使用 super 函数,用子类对象调用父类已被覆盖的方法。
super(Shark,obj).eat() 
obj.eat()
obj.move()

######### 执行结果 ########
Fish 位置坐标是: 8 5
饿了,要吃饭@!
前往 Fishc 位置坐标(8,5), Shark 动物开始进食
太撑了,吃不下了
Fish 位置坐标是: 7 5

特别注意

  • super() 函数是用于调用父类(超类)的一个方法。
  • 当子类与父类定义相同的属性性或方法时, Python 不会删除父类的相关属性或方法而是将父类属性或方法覆盖;子类对象调用的时候会调用到覆盖后的新属性或方法,但父类的仍然还在,只是子类对象“看不到”

5.类多态

在 Python 编程中,多态(Polymorphism) 是指一种对象能够以多种形式出现的能力。具体来说,多态允许不同类的对象可以通过相同的接口调用方法,从而实现相同的操作,提高了代码的灵活性和可扩展性。在 Python 中,多态广泛应用于函数和方法的设计中,使得代码更加直观、易读和维护。

多态通常通过以下几种方式实现:

  1. 方法重写(Method Overriding):子类重写父类的方法,并在运行时根据对象的实际类型调用相应的方法。
  2. 鸭子类型(Duck Typing):在 Python 中,如果一个对象“看起来像鸭子,叫起来像鸭子”,那么它就可以被视为鸭子,即无需显式继承,只要实现了所需的方法即可。
1.方法重写示例

方法重写是多态最常见的实现方式, 子类可以重写父类的方法,并在运行时调用子类的方法。

代码语言:javascript复制
class Animal:
    def sound(self):
        return "Some sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Cat(Animal):
    def sound(self):
        return "Meow"

def make_sound(animal):
    print(animal.sound())

dog = Dog()
cat = Cat()

make_sound(dog)  # 输出: Bark
make_sound(cat)  # 输出: Meow

在这个示例中,make_sound 函数接受一个 Animal 类型的参数,但实际上传入的是 DogCat 对象。在运行时,Python 调用的是 DogCat 类各自实现的 sound 方法。

2.鸭子类型示例

在 Python 中,多态性不仅依赖于继承,还依赖于对象的行为, 这种特性称为鸭子类型(Duck Typing),注意与上例中的方法重写的区别

代码语言:javascript复制
class Dog:
    def sound(self):
        return "Bark"

class Cat:
    def sound(self):
        return "Meow"

class Bird:
    def sound(self):
        return "Chirp"

def make_sound(animal):
    print(animal.sound())

dog = Dog()
cat = Cat()
bird = Bird()

make_sound(dog)  # 输出: Bark
make_sound(cat)  # 输出: Meow
make_sound(bird)  # 输出: Chirp

在这个示例中,DogCatBird 类并没有继承同一个父类,但由于它们都实现了 sound 方法,因此它们可以被传入 make_sound 函数。这就是鸭子类型的一个例子:如果一个对象实现了某个方法,它就可以被当作该方法的类型处理。

6.类重用

在 Python 编程中,类的重用性(Reusability)指的是通过设计和实现类,使其可以在不同的上下文或项目中反复使用,从而避免代码重复,提升开发效率和代码的可维护性。重用性是软件工程中的一个重要原则,能够大大减少开发工作量和维护成本。

Python 中实现类重用性的方式主要有以下几种:

继承(Inheritance):通过继承,可以让一个类(子类)获得另一个类(父类)的属性和方法,从而实现代码重用。

代码语言:javascript复制
class Animal:
    def __init__(self, name):
        self.name = name

    def sound(self):
        return "Some sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Cat(Animal):
    def sound(self):
        return "Meow"

dog = Dog("Buddy")
cat = Cat("Whiskers")

print(dog.name)  # 输出: Buddy
print(dog.sound())  # 输出: Bark
print(cat.name)  # 输出: Whiskers
print(cat.sound())  # 输出: Meow

在这个例子中,DogCat 类继承了 Animal 类,重用了 Animal 类的 __init__ 方法,并实现了各自的 sound 方法。

2.组合(Composition):通过将一个类的实例作为另一个类的属性来实现代码重用,这种方式也称为“has-a”关系。

代码语言:javascript复制
class Engine:
    def start(self):
        return "Engine started"

class Car:
    def __init__(self, brand):
        self.brand = brand
        self.engine = Engine()  # 组合

    def start(self):
        return f"{self.brand} car: {self.engine.start()}"

car = Car("Toyota")
print(car.start())  # 输出: Toyota car: Engine started
代码语言:javascript复制
在这个例子中,Car 类通过组合使用了 Engine 类,从而重用了 Engine 类的 start 方法。

3.模块化(Modularity):将相关的类和函数组织到模块中,可以在不同的程序中重用这些模块。

代码语言:javascript复制
# utils.py
class MathUtils:
    @staticmethod
    def add(a, b):
        return a   b

    @staticmethod
    def subtract(a, b):
        return a - b

# main.py
from utils import MathUtils

print(MathUtils.add(5, 3))  # 输出: 8
print(MathUtils.subtract(5, 3))  # 输出: 2

在这个例子中,MathUtils 类被组织到一个单独的模块 utils.py 中,可以在其他程序中重复使用。

总结:类的重用性是面向对象编程的重要特性之一,通过继承、组合、模块化等技术,可以有效地减少代码重复,提高开发效率和代码的可维护性。在 Python 编程中,注重代码的重用性,可以使你的程序更加简洁、易读和高效。

7.类魔术方法

Python 中的类中的魔术方法(magic methods),也称为特殊方法或双下方法(dunder methods),是一组特殊的方法,它们以双下划线(__)开头和结尾。这些方法允许我们定制类的行为,包括对象的创建、表示、运算符重载等。

简单的说,魔术方法使得 Python 的面向对象编程更加灵活和强大,可以自定义类的各种行为,从而实现运算符重载、容器类型模拟等高级功能。在实际应用中,这些魔术方法可以帮助我们编写更加直观、易读和可维护的代码。

下面是一些常见的类魔术方法及其用途:

  • __init__(self, ...):初始化方法,在创建对象时自动调用。
  • __del__(self):析构方法,在对象被垃圾回收时自动调用。
  • __new__(cls, ...):创建对象时自动调用,用于自定义对象的创建过程。
  • __str__(self):定义当使用 str()print() 函数输出对象时的字符串表示。
  • __repr__(self):定义对象的官方字符串表示,通常用于调试。
  • __call__(self, ...):定义当对象被调用时自动执行。
  • __add__(self, other):定义加法运算符 的行为。
  • __sub__(self, other):定义减法运算符 - 的行为。
  • __mul__(self, other):定义乘法运算符 * 的行为。
  • __mul__(self, other):定义乘法运算符 * 的行为。
  • __truediv__(self, other):定义真除法运算符 / 的行为。
  • __mod__(self, other):定义取模(求余数)运算符 % 的行为。
  • __pow__(self, other):定义幂运算符 ** 的行为。
  • __cmp__(self, other):定义比较运算符 <>== 的行为。
  • __getattr__(self, name):在访问不存在的属性时调用。
  • __setattr__(self, name, value):在设置属性值时调用。
  • __delattr__(self, name):在删除属性时调用。
  • __len__(self):定义对象的长度,使用 len() 函数时调用。
  • __getitem__(self, key):定义按键获取项目的行为,使用 obj[key] 时调用。
  • __setitem__(self, key, value):定义按键设置项目的行为,使用 obj[key] = value 时调用。
  • __delitem__(self, key):定义按键删除项目的行为,使用 del obj[key] 时调用。
1.对象的初始化与销毁

__init__(self, ...):初始化方法,在创建对象时自动调用,用于初始化对象的属性。

代码语言:javascript复制
class MyClass:
    def __init__(self, value):
        self.value = value

obj = MyClass(10)

__del__(self):析构方法,在对象被垃圾回收时自动调用,用于清理资源。

代码语言:javascript复制
class MyClass:
    def __del__(self):
        print("Object is being deleted")
2.对象的表示

__str__(self):定义当使用 str()print() 函数输出对象时的字符串表示。

代码语言:javascript复制
class MyClass:
    def __str__(self):
        return f"MyClass with value {self.value}"

obj = MyClass(10)
print(obj)  # 输出: MyClass with value 10

__repr__(self):定义对象的官方字符串表示,通常用于调试,可以使用 repr() 函数调用。

代码语言:javascript复制
class MyClass:
    def __repr__(self):
        return f"MyClass({self.value})"

obj = MyClass(10)
print(repr(obj))  # 输出: MyClass(10)
3.运算符重载

__add__(self, other):定义加法运算符 的行为。

代码语言:javascript复制
class MyClass:
    def __init__(self, value):
        self.value = value
    
    def __add__(self, other):
        return MyClass(self.value   other.value)

obj1 = MyClass(10)
obj2 = MyClass(20)
result = obj1   obj2
print(result.value)  # 输出: 30

__sub__(self, other):定义减法运算符 - 的行为。

代码语言:javascript复制
class MyClass:
    def __init__(self, value):
        self.value = value
    
    def __sub__(self, other):
        return MyClass(self.value - other.value)

__mul__(self, other):定义乘法运算符 * 的行为。

代码语言:javascript复制
class MyClass:
    def __init__(self, value):
        self.value = value
    
    def __mul__(self, other):
        return MyClass(self.value * other.value)

__truediv__(self, other):定义真除法运算符 / 的行为。

代码语言:javascript复制
class MyClass:
    def __init__(self, value):
        self.value = value
    
    def __truediv__(self, other):
        return MyClass(self.value / other.value)
4.属性访问

__getattr__(self, name):在访问不存在的属性时调用。

代码语言:javascript复制
class MyClass:
    def __getattr__(self, name):
        return f"{name} attribute not found"

obj = MyClass()
print(obj.some_attribute)  # 输出: some_attribute attribute not found

__setattr__(self, name, value):在设置属性值时调用。

代码语言:javascript复制
class MyClass:
    def __setattr__(self, name, value):
        print(f"Setting {name} to {value}")
        self.__dict__[name] = value

obj = MyClass()
obj.attr = 10  # 输出: Setting attr to 10

__delattr__(self, name):在删除属性时调用。

代码语言:javascript复制
class MyClass:
    def __delattr__(self, name):
        print(f"Deleting {name}")
        del self.__dict__[name]

obj = MyClass()
obj.attr = 10
del obj.attr  # 输出: Deleting attr
5.容器类型实现

__len__(self):定义对象的长度,使用 len() 函数时调用。

代码语言:javascript复制
class MyClass:
    def __init__(self, items):
        self.items = items
    
    def __len__(self):
        return len(self.items)

obj = MyClass([1, 2, 3])
print(len(obj))  # 输出: 3

__getitem__(self, key):定义按键获取项目的行为,使用 obj[key] 时调用。

代码语言:javascript复制
class MyClass:
    def __init__(self, items):
        self.items = items
    
    def __getitem__(self, key):
        return self.items[key]

obj = MyClass([1, 2, 3])
print(obj[1])  # 输出: 2

__setitem__(self, key, value):定义按键设置项目的行为,使用 obj[key] = value 时调用。

代码语言:javascript复制
class MyClass:
    def __init__(self, items):
        self.items = items
    
    def __setitem__(self, key, value):
        self.items[key] = value

obj = MyClass([1, 2, 3])
obj[1] = 10
print(obj.items)  # 输出: [1, 10, 3]

__delitem__(self, key):定义按键删除项目的行为,使用 del obj[key] 时调用。

代码语言:javascript复制
class MyClass:
    def __init__(self, items):
        self.items = items
    
    def __delitem__(self, key):
        del self.items[key]

obj = MyClass([1, 2, 3])
del obj[1]
print(obj.items)  # 输出: [1, 3]
6.综合案例

示例.常规魔术方法的使用

  • 案例1:按照以下要求定义一个游乐园门票的类,并尝试计算2个成人 1个小孩平日票价, 面向对象编程的难点在于思维的转换。
代码语言:javascript复制
# 平日票价100元
# 周末票价为平日的120%
# 儿童半票
class Ticket:
  def __init__(self, weekend=False, child=False):
    self.exp = 100
    if weekend:  #根据星期天/儿童来计算
      self.inc = 1.2
    else:
      self.inc = 1

    if child:  #儿童的价格
      self.discount = 0.5
    else:
      self.discount = 1

  def calcPrice(self, num):
    return self.exp * self.inc * self.discount * num  #这里关键点

adult = Ticket()
child = Ticket(child=True)
print("2个成人   1个小孩平日票价为:%.2f" % (adult.calcPrice(2)   child.calcPrice(1)))

############### 执行结果 #################
# 2个成人   1个小孩平日票价为:250.00
  • 案例2.当实例化一个对象,count 变量 1, 当销毁一个对象变量自动-1 (注:首次执行)
代码语言:javascript复制
class Count:
    count = 0 
    def __init__(self):    # 实例化对象时触发该魔术方法
        Count.count  = 1
    def __del__(self):     # 销毁对象时触发该魔术方法
        Count.count -= 1
    def getCount(self):
        print("当前 count 的 %d 值" %Count.count)

a = Count() 
b = Count()
c = Count()
print("Count 类实例化次数:",Count.count)
d = a  #  d 对 a 得引用
e = a  #  d 对 a 得引用
print("Count 类实例化次数:",Count.count)
del d  #注意:这里不会触发del  (只有在所有得引用被删除后才能触发del)
del e  # 同样不能触发
del a  # 触发 del 魔术方法
print("Count 类实例化次数:",Count.count)

############### 执行结果 #################
# Count 类实例化次数: 3
# Count 类实例化次数: 3
# Count 类实例化次数: 2
  • 案例3.CapStr 类继承一个不可变得字符串类,使用__new__魔术方法,将字符串全部变成大写。
代码语言:javascript复制
class CapStr(str):
  def __new__(cls,string):
    string = string.upper()        # 将不可变类型的字符串,全部字母变成大写
    return str.__new__(cls,string) # 返回修改后得字符给对象

a = CapStr('I love Study Python3!')
print(a)

############### 执行结果 #################
# I LOVE STUDY PYTHON3!
  • 案例4.魔术方法__str____repr__的区别。
代码语言:javascript复制
#!/usr/bin/python
class Demo:
    def __str__(self):
        return '我是__str__魔术方法,需要print()输出'
    
class Demo1:
    def __repr__(self):
        return '2 - 我是__repr__魔术方法,直接对象输出'
  
a = Demo()
print("1 -",a)

b = Demo1()
print(b)  #在>>> b 可以直接输出

################ 执行结果 #################
# 1 - 我是__str__魔术方法,需要print()输出
# 2 - 我是__repr__魔术方法,直接对象输出

示例.类关于计算的魔术方法案例

描述:在 Py2.2 以前类和类型是分开的(实际是类和属性的封装),但是在之后作者进行了统一(将Python类型转换成为工厂函数),例如: 工厂函数其实就是一个类对象,当你调用他们的时候,事实上就是创建一个相应的实例对象。

代码语言:javascript复制
#实际工程函数就是类对象
>>> type(len)
<class 'builtin_function_or_method'>  #内置函数

>>> type(int)   #类型都是类对象
<class 'type'>
>>> type(tuple)
<class 'type'>

>>> class C:
...  pass
...
>>> type(C)  #类定义
<class 'type'>
# Py2.2 以前
# int('123') #实际是调用了int并返回一个整形值 
# py2.2 以后
a = int('123')  #其实就是一个实例化过后的对象

示例1.使用魔术方法进行四则运算,实现类似于C 的运算符重载

代码语言:javascript复制
#!/usr/bin/python
# 继承 int 类
class Newint(int):
    def __add__(self, value):
        return super().__add__(value)  # 方法1.super是超类指代父类
    def __sub__(self, value):
        return int(self) - int(value)  # 方法2.必须要强制类型转换,如过向以下则会报错递归循环异常
        #return self - value    # RecursionError: maximum recursion depth exceeded in comparison
    def __and__(self, value):       #该魔术方法 = &  
        return super().__and__(value)

a = Newint(8)
b = Newint(6)

print("a   b =",a   b)
print("a - b =",a - b)
print("a & b =",a & b)

# 执行结果:
a   b = 14
a - b = 2
a & b = 0

案例2. 类的反算法运算魔术方法

代码语言:javascript复制
#!/usr/bin/python

# 案例1:反运算符
class Newint(int):
    def __radd__(self, value):  #反运算 
        return int.__sub__(value,self)  #方法1:执行为减,value self 顺序会影响到 谁是减数 / 被减数

    def __rsub__(self, value):  #反运算
        return super().__add__(value)  #方法2:执行为减 

    def __rmul__(self, value):
        return super().__truediv__(value)

a = Newint(5)
b = Newint(3)

print(a   b)  # 由于对象 a 可以支持   / - , 所以不会触发反运算,输出 8
print(a - b)  # 由于对象 a 可以支持   / - , 所以不会触发反运算,输出 2 

# 由于 1 是非对象 则采用 b 的处理方法
print("1   b =",1   b)  #  1 - 3 => -2 由于改变了value,self顺序
print("1 - b =",1 - b)  # 触发反运算 => 3   1 = 4 
print("1 * b =",5 * b)  # 触发反运算 => 3 / 5 = 0.6


# 案例2:一元运算符
class OneInt(int):
    def __pos__(self):  #定义负号行为 -x
        return super().__pos__()  # -(self)

a = OneInt(-1)
print("-(a) =",-a)   #此时触发一元运算符 -(-1)

######### 执行结果 ########
# 8
# 2
# 1   b = -2
# 1 - b = 4
# 1 * b = 0.6
# -(a) =  1

特别注意:

  • __init__ 方法不应当返回除了 None 以外的任何对象。
  • __add__ 方法中不应直接return self other会导致无限递归(深坑)。
  • __radd__ 方法中反运算需要注意 运算符支持前后顺序。

示例.类属性访问魔术方法

案例1.通过类的属性来设置与调用方法;

代码语言:javascript复制
#!/usr/bin/python

# 案例1:
class AttrView:
    def __getattribute__(self, name):
        print("调用 getattribute 魔法方法")
        return super().__getattribute__(name)  #super()自动获取基类,这个类没有继承类默认是object类

    def __getattr__(self,name):
        print('调用 getattr 魔法方法')

    def __setattr__(self,name,value):
        print("调用 setattr 魔法方法")
        super().__setattr__(name,value)
    
    def __delattr__(self, name):
        print('调用 delattr 魔法方法')
        super().__delattr__(name)

demo = AttrView()
demo.x              # 尝试不存在属性的时候触发 调用 getattribute / getattr 这两个魔法方法
setattr(demo,'x',1) # 设置属性 调用 setattr 魔法方法
demo.y = 1          # 设置属性 调用 setattr 魔法方法
demo.y              # 获取属性 调用 getattribute 魔法方法
delattr(demo,'y')   # 删除属性 调用 delattr 魔法方法


#  案例2:
class Rectangle:
    def __init__(self, width = 0, height = 0):
        self.width = width   #会触发__setattr__魔术方法
        self.height = height

    def __setattr__(self, name, value):
        if name == 'square':  #正方形
            self.height = value
            self.width = value
        else:
            super.__setattr__(self, name, value)   # 方法1:防止无限递归错误 (建议采用基类的setattr方法)
            #self.__dict__[name] = value    # 方法2 

    def getArea(self):
        return self.width * self.height

    def getPeri(self):
        return (2 * (self.width)    2 * (self.height))

r1 = Rectangle(4,5)
print("矩形面积:",r1.getArea())

r1.square = 10 #建立一个属性表明是正方形
print("正方形面积: ",r1.getArea())
print("正方形周长:",r1.getPeri())
print("__dict__",r1.__dict__)    #将类的全部属性放返回字典类型

########## 执行结果 ####################
# 矩形面积: 20
# 正方形面积:  100
# 正方形周长: 40
# __dict__ {'width': 10, 'height': 10}

示例.定制序列的魔术方法

描述:协议(Protocols)与其他的编程语言中的接口很相似,它规定您那些方法必须要定义;然而在Python中的协议就显得不那么正式;事实上更新是一种指南;

要求:编写一个不可改变的自定义列表,要求记录每个元素被访问的次数;

代码语言:javascript复制
#!/usr/bin/python3
# -*- coding:utf-8 -*-

#功能:容器序列定制类型协议()
class Countnum:
    def __init__(self,*args):
        self.value = [x for x in args]  # 列表表达式
        self.count = {}.fromkeys(range(len(self.value)),0)

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

    def __getitem__(self,index):
        self.count[index]  = 1       # 访问次数 1
        return self.value[index]     # 返回下标的值

a = Countnum(1,3,5,7,9)
b = Countnum(2,4,6,8,10)

print(a[1],b[1])
print(a[1],b[1])
print("两个对象数列之和:",a[3] b[3])

print("A对象访问的次数:",a.count)
print("B对象访问的次数:",b.count)

############ 执行结果 ################
# $ python demo3.23.py
# 3 4
# 3 4
# 两个对象数列之和: 15
# A对象访问的次数: {0: 0, 1: 2, 2: 0, 3: 1, 4: 0}
# B对象访问的次数: {0: 0, 1: 2, 2: 0, 3: 1, 4: 0}

8.类描述符

描述:描述符就是将某种特殊类型的类的实例指派给另外一个类的属性,比如property() 是一个比较奇葩的BIF,它的作用把方法当作属性来访问,从而提供更加友好访问方式;

描述符就是一个类,一个至少实现 __get__()、__set__() 或 __delete__() 三个特殊魔术方法中的任意一个的类。

代码语言:javascript复制
#!/usr/bin/python
#类属性 - 描述符

#定义一个类,为了实现原生的property原理必须使用下面的三个魔术方法
#案例1
class Decriptor:
    def __get__(self,instance,owner):
        print("getting ... ",self, instance, owner) #参数分别代表 (Decriptor本身类 , 类对象test , Test类本身)

    def __set__(self,instance,value):
        print("setting ... ",self, instance, value)

    def __delete__(self,instance):
        print("deleting ...",self,instance)        

class Test:
    x = Decriptor()  #Decriptor() 类的实例 / 又叫属性x的描述符

test = Test()
test.x
test.x = 'ceshi'
del test.x 

############ 执行结果 #######
# getting ...  <__main__.Decriptor object at 0x000002443D18E908> <__main__.Test object at 0x000002443D18E940> <class '__main__.Test'>
# setting ...  <__main__.Decriptor object at 0x000002443D18E908> <__main__.Test object at 0x000002443D18E940> ceshi
# deleting ... <__main__.Decriptor object at 0x000002443D18E908> <__main__.Test object at 0x000002443D18E940>


#案例2:自定义property
class MyProperty:
    def __init__(self, fget=None, fset=None,fdel=None):  #其他类的三个方法
        self.fget = fget
        self.fset = fset
        self.fdel = fdel

    def __get__(self,instance,owner):
        return self.fget(instance)      #传入实例对象的方法

    def __set__(self,instance,value):
        self.fset(instance,value)
    
    def __delete__(self,instance):
        self.fdel(instance)


class C:
    def __init__(self):
        self._x = None
    
    def getX(self):
        return self._x

    def setX(self, value):
        self._x = value

    def delX(self):
        print("delete 销毁属性!")
        del self._x
    
    #x 对象的描述符 (传入类里面的三个方法)
    x = MyProperty(getX,setX,delX)   #类实例

c = C()
c.x = 'Property'
print(c.x,c._x)
del c.x

######################
# Property Property
# delete 销毁属性!

9.类修饰符(装饰器)

描述:在 Python 中,类修饰符(Class Decorators)是用于修饰类的函数。它们可以用来修改类的定义或者扩展类的功能。类修饰符的使用方式类似于函数修饰符,只不过它们应用于类上。修饰符是一个很著名的设计模式,常见的应用场景包括日志记录、方法添加、数据验证等。通过合理使用类修饰符,可以使代码更加简洁、可维护和可扩展。

实际上,修饰符就是一种优雅的封装,可在模块或类定义内的函数进行修饰; 一个修饰符就是一个函数, 它将被修饰的函数(紧邻的下一行)将传递做为参数,并返回修饰后的同名函数或其它可调用的东西。

基本语法:

代码语言:javascript复制
def class_decorator(cls):
    # 修改或扩展类的定义
    return cls

@class_decorator
class MyClass:
    pass

在 Python 类中,修饰符的语法与函数修饰符的语法类似,只是修饰符在类定义的前一行,除开之外还有三个内置修饰符,可将类中定义的方法变成静态方法( staticmethod ), 类方法 (classmethod)类属性 (property)

  • @property :属性修饰符
  • @classmethod:类方法修饰符
  • @staticmethod:静态方法修饰符,注意与之相邻的类方法中参数不能带self。

其中静态方法的优点是,不会绑定到实例对象上,换而言之就是节省开销,这也意味着并不需要 self 参数,因此即使是使用对象去访问,self 参数也不会传进去。

基础示例

  • 示例1.例子中,log_class 修饰符通过创建一个新类 Wrapped 来扩展原始类 MyClass,在实例化时打印日志信息。
代码语言:javascript复制
#!/usr/bin/python
# coding=utf-8
def log_class(cls):
    class Wrapped(cls):
        def __init__(self, *args, **kwargs):
            print(*args)
            print(f"Creating instance of {cls.__name__}")
            super().__init__(*args, **kwargs)
    return Wrapped

@log_class
class MyClass:
    def __init__(self, value):
        self.value = value

instance = MyClass("公众号:全栈工程师修炼指南")
# 输出: 
# 公众号:全栈工程师修炼指南
# Creating instance of MyClass
  • 示例2.使用多个修饰符来修饰同一个类,修饰符的应用顺序是自下而上的。
代码语言:javascript复制
# 例子中,add_method 修饰符向 MyClass 类添加了一个新的方法 new_method 。
def decorator1(cls):
    print("Applying decorator1")
    return cls

def decorator2(cls):
    print("Applying decorator2")
    return cls

@decorator1
@decorator2
class MyClass:
    pass

# 输出:
# Applying decorator2
# Applying decorator1
  • 示例3.给类添加一个新的方法。
代码语言:javascript复制
def add_method(cls):
    def new_method(self):
        return f"New method in {self.__class__.__name__}"
    cls.new_method = new_method
    return cls

@add_method
class MyClass:
    def __init__(self, value):
        self.value = value

instance = MyClass(10)
print(instance.new_method())  
# 输出: New method in MyClass
  • 示例4.使用类修饰符,可以定义一个装饰器来打印函数执行时间。
代码语言:javascript复制
class timeslong1:
    def __init__(self,func):
        self.f = func  #传入的实际是f() 函数

    def __call__(self):
        start = time.perf_counter()
        print("It's time starting ! ")
        self.f()
        print("It's time ending ! ")
        end = time.perf_counter()
        print("It's used : %s ." % (end - start))

@timeslong1  #将下面的函数或者类作为自己的参数
def f():
    y = 0
    for i in range(3):
        y = y   i   1
        print(y)
    return y
 
f()  #调用时候触发@修饰符

######### 执行结果.START########
# It's time starting !
# 1
# 3
# 6
# It's time ending !
# It's used : 0.00160638099999999 .
######### 执行结果.END########
  • 示例 5.在类初始化时进行数据验证。
代码语言:javascript复制
# 例子中,validate 修饰符修改了 MyClass 的 __init__ 方法,在初始化时对传入的参数进行数据类型验证
def validate(cls):
    original_init = cls.__init__

    def new_init(self, *args, **kwargs):
        for key, value in kwargs.items():
            if not isinstance(value, (int, float)):
                raise ValueError(f"Expected int or float, got {type(value).__name__}")
        original_init(self, *args, **kwargs)
    
    cls.__init__ = new_init
    return cls

@validate
class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

try:
    instance = MyClass(x=10, y="20")
except ValueError as e:
    print(e)  # 输出: Expected int or float, got str
  • 示例 6.类中三种内置修饰符,实例演示
代码语言:javascript复制
#!/usr/bin/python3

# 案例1.类方法、静态方法效果示例。
class Hello:
  def __init__(self):
    pass
  
  # 方式1:正是因为设置静态方法和类方法过于讨人吐槽,因此 Python 的作者才开发出了函数修饰符的形式替代。
  def foo(cls):
    print("旧方法:使用 classmethod 设置为函数为类方法,调用类方法 foo()")
  foo = classmethod(foo)    # 将 foo() 方法设置为类方法

  # 方式2.类方法修饰符
  @classmethod
  def print_hello(cls):
    print("类方法修饰符:调用类方法 print_hello()",)
 
  # 方式3.静态方法修饰符 (注意这里的巨坑 self )
  @staticmethod
  def static_hello(arg):
    return "静态方法修饰符:调用静态方法 static_hello Value ="   str(arg)

# 直接使用 类名.方法名() 调用类方法
Hello.foo()
Hello.print_hello() 
print(Hello.static_hello(1)," 对象:",Hello.static_hello)

############### 执行结果 ################
# 旧方法:使用 classmethod 设置为函数为类方法,调用类方法 foo()
# 类方法修饰符:调用类方法 print_hello()
# 静态方法修饰符:调用静态方法 static_hello Value =1  对象: <function Hello.static_hello at 0x766f5d147f60>

# >>> c1 = Hello()
# >>> c2 = Hello()
# # 静态方法只在内存中生成一个,节省开销
# >>> c1.static_hello is C.static_hello       #True
# >>> c1.nostatic is C.nostatic   # False
# >>> c1.static_hello        # <function Hello.static_hello at 0x000001F2DB73C950>
# >>> c2.static _hello       # <function Hello.static_hello at 0x000001F2DB73C950>
# >>> Hello.static_hello     # <function Hello.static_hello at 0x000001F2DB73C950>

# # 普通方法每个实例对象都拥有独立的一个,开销较大
# >>> c1.nostatic   # <bound method  Hello.nostatic of <__main__.C object at 0x03010590>>
# >>> c2.nostatic  # <bound method  Hello.nostatic of <__main__.C object at 0x032809D0>>
# >>> Hello.nostatic   # <function  Hello.nostatic at 0x0328D2B8>


# 案例2:内置属性修饰符效果示例
class C:
    def __init__(self, size=10):
        print("初始化类属性",size)
        self.size = size
    
  # 属性修饰符,在类中定义一个属性,并绑定到类中
    @property   #关键点 类属性 / 绑定的属性是x
    def x(self):
        return self.size

    @x.setter 
    def x(self, value):
        print("设置类属性 x",value)
        self.size = value

    @x.deleter
    def x(self):
        print("删除类属性 x")
        del self.size

demo = C()
print("获取属性的值:",demo.x)        # 获取属性的值: 10
demo.x = 1024
print("获取更改后属性的值:",demo.x) # 获取更改后属性的值: 1024
del demo.x

############## 执行结果 ###############
# 初始化类属性 10
# 获取属性的值: 10
# 设置类属性 x 1024
# 获取更改后属性的值: 1024
# 删除类属性 x

如果此篇文章对你有帮助,请你将它转发给更多的人!

0 人点赞