🗓️ 2025-05-13 🎖️ python 🗂️ 代码

稍微深入一些Python中的类(class)

前言

**类(class)**在python代码中几乎无处不在,但在近日的学习中发现,我对它真是了解甚少,甚至基础结构都不能熟稔于心,故开此笔记认真学习。和我一起重新认识一下它吧。

一个简单的类

#code 1
class Dog:
    # 类属性
    species = "Dog"
    # 初始化方法
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # 实例方法
    def bark(self):
        return print("旺旺")

print(mydog.species)
print(mydog.name,mydog.age)
mydog.bark()

#输出为:
#Dog
#doudou 2
#旺旺

是一种对数据进行计算操作的蓝图,离不开属性方法

code 1中,我们定义了一个 Dog 类,并对它进行了实例化,生成了一个对象。

这个类的结构,很简单。

首先是放在第一部分的类属性类属性直接在类中定义变量。所有通过这个类生成的对象都具有这些属性。

然后是放在第二部分的诸多方法,其实就是一个个的函数。

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

在一般的实例方法中,也可以设置参数,定义属性。我们看一段新的代码:

#code 2-1
class phone:
    owner = "xiyangyang"
    def __init__(self,type="huawei"):
        self.type = type
    def call(self,number=10086):
        self.number = number
        print("the number is {}".format(self.number))
print(phone.owner)
myphone = phone("apple")
myphone.call(110)
print("what number is called? ",myphone.number)

这里在phone类的call方法下,定义了一个实例属性,并需要一个输入number指定值。

但实例方法的参数不一定都是实例属性,且看以下代码:

#code 2-2
class phone:
    def __init__(self,type="huawei"):
        self.type = type
    def call(self,number=10086):
        print("the number is {}".format(number))
myphone = phone("apple")
myphone.call(110)

这样定义call方法,在使用的时候也需要传入一个number,但是这个number不是这个实例的属性,无法随时查看。

这就是说,对于类中的方法,目前涉及的参数有三类,一个是默认参数self,一个是实例属性参数,一个是普通参数

  1. 截至目前,我们已经了解了一个简单类的结构。即:声明类属性,声明实例方法(其中涉及到添加实例属性)。
  2. 由类生成实例时的参数接口,由魔法方法__init__定义,而一般实例方法的接口,在调用方法的时候才会出现。这也就意味着,__init__方法中定义的实例属性,是伴生的,只要生成实例就存在。而随一般实例方法定义的实例属性,则需要首次调用后才存在。

魔法方法

在python中,所有被双下划线包围的方法,统称为魔法方法。太多了,我这里只记录我遇到的,不定时更新。

1.__init__方法

这个魔法方法用于类的初始化。规定了实例化时接受的参数。

2.__len__方法

#code 3-1
class menu:
    restaurant="hepingfandian"
    def __init__(self,foods):
        self.foods = foods
    def __len__(self):
        count = 0
        for food in foods:
            count = count + 1
        return count
mymenu = menu(["宫保鸡丁","鱼香肉丝","米饭"])
print(len(mymenu))
#输出:3

这个魔法方法让实例可以被用len()函数统计长度,核心是要返回一个整数,至于这个整数是如何计算得到的,这部分内容就由自己定义了。

3.__getitem__方法

#code 3-2
class menu:
    restaurant="hepingfandian"
    def __init__(self,foods):
        self.foods = foods
    def __getitem__(self,key):
        return 10
mymenu = menu(["宫保鸡丁","鱼香肉丝","米饭"])
print(mymenu["霸王餐"])
#输出:10

这个魔法方法可以让实例像字典或列表一样,实现键值对和索引切片的功能。具体来说,就是根据条件返回不同的值。不然就像上述代码一样,客人要吃霸王餐,对应的值却是10。

#code 3-3
class menu:
    restaurant="hepingfandian"
    def __init__(self,foods):
        self.foods = foods
    def __getitem__(self,key):
        if not isinstance(key,str):
            raise TypeError("Attribute key must be a string")
        
        if key == "霸王餐":
            return "你吃牛魔"

        for food in self.foods:
            if food == key:
                return self.foods[key]
        raise KeyError(f"Food '{key}' does not exist in menu.")
        
mymenu = menu({"宫保鸡丁":25,"鱼香肉丝":10,"米饭":2})
print(mymenu["霸王餐"])
print(mymenu["米饭"])
#输出:你吃牛魔
#输出:2

__getitem__方法可以为实例提供独特的接口,就是[参数]。这里的参数可以是字符串、数字或者切片,使用起来像字典或者列表。而一般方法的使用则要.onemethod(参数1,参数2,...)

#code 3-4
class top3:
    ip = "harbin"
    def __init__(self,school):
        self.school = school
    def __getitem__(self,index):
        if isinstance(index,int):
            return self.school[index]
        if isinstance(index,slice):
            return self.school[index]
Top3InMyheart = top3(["qinghua","beida","hagongda"])

print("who is top3?",Top3InMyheart[0:3])
print(Top3InMyheart.ip)

这段代码定义了一个名叫top3类,实现了索引和切片功能,同时可以这个类的实例拥有列表不同的功能,即这个类的实例有一个类属性:ip。让我们可以得知这个排名来自harbin

4.__setitem__方法

#code 3-5
class games:
    def __init__(self):
        self.games={}
    def __setitem__(self,key,value):
        self.games[key]=value
    def __getitem__(self,key):
        return self.games[key]

MyLoveGames = games()
MyLoveGames[1] = "原神"
print(MyLoveGames[1])
#输出:原神

这个魔法方法让类的实例可以像字典或者列表一样,添加新的键值对或者添加新的索引和值。

5.__delitem__方法

#code 3-6
class games:
    def __init__(self):
        self.games={}
    def __setitem__(self,key,value):
        self.games[key]=value
    def __getitem__(self,key):
        return self.games[key]
    def __delitem__(self,key):
        del self.games[key]

MyLoveGames = games()
MyLoveGames[1] = "原神"
MyLoveGames[1] = "DOTA2"
print(MyLoveGames[1])
del MyLoveGames[1]
#输出:DOTA2

这个魔法方法可以让类的实例使用del关键字来删除键或索引。

6.__iter__方法

#code 3-7
class games:
    def __init__(self):
        self.games={}
    def __setitem__(self,key,value):
        self.games[key]=value
    def __getitem__(self,key):
        return self.games[key]
    def __delitem__(self,key):
        del self.games[key]
    def __iter__(self):
        for game in self.games:
            yield game
            
            
MyLoveGame = games()
MyLoveGame[0] = "原神"
MyLoveGame[3] = "DOTA2"
MyLoveGame[2] = "明日方舟"
del MyLoveGame[2]
for game in MyLoveGame:
    print(game)
#输出:0
#输出:3

这个魔法方法让实例变为可迭代对象,可以进行for循环。也可以用iter()函数生成迭代器。这里涉及到一个头疼的yield关键字,又涉及到生成器迭代器。还是另开一文来记录吧。

简单来讲,当一个可迭代对象遇到for循环事件时,会自动转到它的__iter__方法,又因为这里的__iter__方法含有yield关键字,所以不会立即执行,而是得到了一个生成器对象。

紧接着,for循环又根据这个生成器对象的__next__方法(注意,这里是生成器对象的__next__方法,而非是,我们这个自定义类的__next__方法,其实,我们这里也没有写__next__方法)把得到的值赋值给for game in MyLoveGame:中的game,然后打印。

在进行第二次循环的时候,同样地,再次进入实例的__iter__方法,得到同一个生成器对象。并对这个生成器对象再此施加__next__方法,并把得到的值赋值给for game in MyLoveGame:中的game,然后打印。

__iter__方法本质上定义了一个生成器对象,而非函数。for循环会不断调用这个生成器对象__next__方法并把得到的值传递给game

7.__repr__方法

#code 3-8
import collections.abc
class games(collections.abc.MutableMapping):
    def __init__(self):
        self.games={}
    def __len__(self):
        return 0
    def __setitem__(self,key,value):
        self.games[key]=value
    def __getitem__(self,key):
        return self.games[key]
    def __delitem__(self,key):
        del self.games[key]
    def __iter__(self):
        for game in self.games:
            yield game
    def __repr__(self):
        return repr(dict(self))
            
MyLoveGame = games()
MyLoveGame[0] = "原神"
MyLoveGame[1] = "DOTA2"
MyLoveGame[2] = "明日方舟"

print(MyLoveGame)
#输出:{0: '原神', 1: 'DOTA2', 2: '明日方舟'}

这里让自定义的games继承了一个抽象基类,这样return repr(dict(self))才不报错。

这个魔法方法让实例可以直接使用实例时,返回一个官方字符串。

继承与抽象基类

抽象基类只能被继承。它要求子类必须实现某些抽象方法

Comment