前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python3 | 练气期,面向对象、类魔术方法,类修饰器!

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

作者头像
全栈工程师修炼指南
发布2024-07-29 10:02:36
1380
发布2024-07-29 10:02:36
举报
文章被收录于专栏:全栈工程师修炼之路
[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ]

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈工程师修炼指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 前言简述
  • 0x01 Python 面向对象编程
    • 1.类和对象
      • 2.类封装
        • 3.类继承
          • 4.类重写
            • 5.类多态
              • 1.方法重写示例
              • 2.鸭子类型示例
            • 6.类重用
              • 7.类魔术方法
                • 1.对象的初始化与销毁
                • 2.对象的表示
                • 3.运算符重载
                • 4.属性访问
                • 5.容器类型实现
                • 6.综合案例
              • 8.类描述符
                • 9.类修饰符(装饰器)
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档