前文讲过,在Python中“万物皆对象”。每一个常量、变量、函数,它的背后都是一个个类。除了这些自身和各种第三方库中定义的类,我们也可以自己定义类;从而对程序实现更好的封装。
类的简介
类包括类变量、实例变量和函数。一个类的所有对象的类变量都是相同的值,在构造函数__init()__外进行创建;而实例变量就和C++类似,在__init()__中进行创建。例如:
class A:
m=2 #类变量
def __init__ (self, a, b):
self.a=a #实例变量
self.b=b #实例变量
obj1=A(20,30)
obj2=A(10,20)
print(obj1.a, obj1.b) #20,30
print(A.m, obj1.m) #2,2
A.m=5
print(A.m, obj1.m, obj2.m) #5,5,5
__init()__是类A的构造函数,创建对象时会执行这部分来创建。self作为参数传入函数,表示当前对象本身。这和C++中的this指针很相似。
在部分项目代码中,可能会借助dataclasses修饰器完成类的创建;这能极大方便类的创建:
from dataclasses import dataclass
@dataclass
class A:
a:int
b:int
obj1=A(20,30)
obj2=A(10,20)
print(obj1.a, obj1.b)
使用类可以很方便的对程序进行封装。例如我们设计了一个聊天机器人,其通过调用不同的大语言模型实现问答、翻译等多种任务。我们可以定义一个LLM类实现封装:
from gpt import GPT
from Ernie import ErnieBot
class LLM:
def __init__(self, model):
self.model=model
def get_QA(self, question):
prompt=f'''请回答问题: 问题:<{question}>'''
ans=self.model(prompt, "QA")
return ans
def translation(self, query):
prompt=f'''你的任务是把<>中的语言翻译为中文。文本:<{query}>'''
ans=self.model(prompt, "Translate")
return ans
llm1=LLM(GPT)
llm2=LLM(ErnieBot)
ans=llm1.get_QA("介绍一下Python")
在这个例子中,我们LLM类的实例变量被定义为模型类型(GPT或ErnieBot)。用户通过传入选择的模型类型来选择模型、选择任务等。
类的继承
假设我们定义新的类B是前面的类A的子类。这样写在构造子类B时会自动调用A的构造函数,并赋值实例变量c。
class B(A):
def __init __(self, a, b, c):
super().__init __(a,b)
self.c=c
父类与派生类之间还存在同名函数的重写,子类会覆盖父类的同名函数:
from dataclasses import dataclass
@dataclass
class A:
a:int
b:int
def print_args(self):
print(self.a, self.b)
obj1=A(20,30)
print(obj1.a, obj1.b) #20,30
@dataclass
class B(A):
def print_args(self):
print("this is in class B:",self.a, self.b)
obj2=B(20,30)
obj2.print_args() #this is in class B:20,30
在C++或JAVA中,类变量、方法(函数)、继承关系等被分为公有、私有、保护等。这对类的访问进行了严格限制,避免泄露。
python的类变量/方法默认都是公有的,公有继承。如果需要私有变量/方法,可以在变量前加_ _;这钟情况下,派生类也不能访问私有变量。但严格意义上python没有私有变量,其仍可在类外通过 _ 类名 _ _变量名 进行访问。
囿于笔者水平有限,这部分就不详细展开介绍了。以笔者有限的经验来看,许多AI学术项目的开源代码中均会使用类来让程序结构清晰,但很少出现私有类。而需要私有类的项目开发类项目似乎鲜有用到python,一般会使用C++, C#, Java等更适合开发的面向对象语言。