Python类工厂
类工厂顾名思义就是创造类的工厂(函数),也就是函数的返回值是一个类对象。我们可以使用这个类对象生成实例。而每一次执行函数都会得到一个"不同"(地址不同)的类对象,我们可以用不同的变量去接收这些类对象,并使用这些个类对象完成实例化得到类的实例。因此类工厂最大的作用就是,可以不用在执行前(编码时)就确定好我的类需要有哪些属性,有哪些方法,而是在执行过程中根据值执行结果自动生成我们所需要的类,这个类包含着我们需要的属性和方法(其实这里可以深刻的体会出什么叫做Python是一个动态语言)。
1、类工厂函数
我们在元类这一章(Python元类)讲到了我们可以通过type(类名, (object,), {...})的方式创造一个类对象,进而通过类对象可以得到对应的实例对象。采用这种元类方法创建对象可以自主的确定类的成员(函数/属性),如果我们把这个过程封装进一个函数里,当调用函数的时候,就返回一个类对象,那么这个函数就是类工厂函数。例如下面这个代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def create_class(): """类工厂返回类对象, 注意返回的是类, 并不是类的实例""" class People( object ): def __init__( self , name, age): self .name = name self .age = age def study( self ): print (f "{self.name}正在学习" ) return People ## 这里是两个类对象, 类似于以下两个语句 # People1 = type("People", (object,), {...}) # People2 = type("People", (object,), {...}) People1 = create_class() People2 = create_class() |
我们定义的create_class()函数里面就只有一个操作,那就是通过class关键字声明了一个类,并把这个类对象返回了。这里需要有元类的了解,其实函数内部每次都是在执行type("People", (object,), {...}),并把执行得到的类对象返回了,只不过采用了class关键字的方式去完成这个过程罢了。我们看上述代码的最后两行,我们使用了两个变量People1和People2去接收函数的返回值,意味着这里的变量People1和People2指向的其实时类对象,那么我们可以使用这两个变量创造实例了。
1 2 3 4 5 6 7 8 | print ( id (People1) = = id (People2)) print (People1) print (People2) person1 = People1( "小明" , 13 ) person2 = People2( "小华" , 14 ) print ( isinstance (person1, People1)) # true print ( isinstance (person2, People1)) # false |
我们首先看一看这两个类对象的地址是不是一样的,再分别使用People1和People2去创造了两个实例,紧接着使用isinstance来判断实例与类之间的关系,执行结果如下:
可以看到首先,People1和People2指向的类对象地址是不一样的,着意味着尽管是通过同一个函数得到的类对象,但实际上是两个不同的类对象,虽然都叫People(这里的People是类的名字)。下面我们分别使用People1和People2创造了两个实例(People1创造的实例person1,People2创造的实例person2),并使用isinstance来判断实例与类的关系。我们发现,person2通过isinstance判断出与People1是没有关系的。这也进一步说明,实际上通过类工厂函数创造出的类对象,其实没有任何关系,这里来看只不过是两个person的包含的功能一样罢了。
2、类工厂函数使用场景
类工厂函数有什么用呢?一般情况下使用类工厂函数,是为了能根据传递进函数的参数,来选择性的构建类对象。比如有个需求创建People类,Dog类,Car类,每一个类都有自己的初始化属性,比如People类需要name和age,Dog类需要name和color,Cat类需要brand等。一种很容易想到的方式就是我直接创建三个类,需要用哪个就用哪个。我们能不能进行整合呢?比如我先把这些信息配置到一个文件中,并通过类工厂函数的参数,从文件中得到我们需要的初始化属性,并创建一个类对象返回呢?看下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import csv def create_class(classname): params = [] ## 用于记录需要由类初始化时需要由那些属性 with open ( "类配置.csv" , "r" , encoding = "utf-8-sig" ) as csv_file: for row in csv.reader(csv_file): # 按行读取, row为列表, 以,分歌元素 if row = = [] or row[ 0 ] ! = classname: ## 如果不是属于该类的属性则跳过 continue params.append(row[ 1 ]) class Animal( object ): expected_param = params # 类变量 def __init__( self , * * kwargs): if set ( self .expected_param) ! = set (kwargs.keys()): raise ValueError(f "初始化属性与类不匹配, 需要有属性{self.expected_param}" ) for k, v in kwargs.items(): setattr ( self , k, v) ## 采用这种方式取代 self.属性名 = 属性值 的方式进行实例对象初始化(因为k,v都是字符串) def print_info( self ): info_str = "对象具有以下属性: " for key in self .expected_param: ## self.xxx也能访问类属性 info_str + = f "{key}={getattr(self, key)} " return info_str return Animal # 返回类 |
代码的第5-9行通过读取“类配置.csv”这个文件的信息,并根据传入的classsname得到我们需要创建的类的信息。其中"类配置.csv"文件如下:
第一行列表示类的名称,第二列表示对应类需要那些初始化属性。代码的11到24行则是根据这些属性动态的创造了一个类,需要注意的是由于我们编码时并不知道要创造哪个类,因此我们在__init__中使用setattr(self,属性名,属性值)的方式创造属性(也就是代码中18行所作的操作)。我们测试一下:
1 2 3 4 5 6 7 8 | MyClass1 = create_class( "People" ) MyClass2 = create_class( "Car" ) my_object1 = MyClass1(name = "小明" , age = 14 , gender = "男" ) ## 用MyClass1类创造对象 my_object2 = MyClass2(money = 20000 , color = "红色" , brand = "宝马" ) ## 用MyClass2类创造对象 print (my_object1.print_info()) print (my_object2.print_info()) |
我们传入不同的参数,执行create_class()类工厂函数,得到了两个类对象。并使用其分别创建了对应的实例对象,并执行实例对象的print_info()方法:
可以看到,确实正确执行了。
3、总结
当我们需要在运行时确认类的成员有哪些而不是在编码时,类工厂的作用就显现出来了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南