如何动态创建新的Python类文件?
我现在有一个包含超过200条记录的CSV文件,每一行都需要变成一个独立的类文件。这些类会从一个基础类继承一些字段变量,并根据CSV文件中的内容设置这些变量的值。此外,Python模块的名称也需要根据CSV文件中的某一条记录来命名。
我真的不想手动创建超过200个单独的Python类文件,所以我在想有没有什么简单的方法可以做到这一点。谢谢!
补充一下* 我其实更擅长Java和C#,对Python不太熟悉。 更多细节是:我正在尝试为一个已经存在的网页游戏创建一个AI,我可以通过一个实时文本框提取实时数据。 玩家每回合可以使用超过200种不同的动作,每个动作都非常不同。我可能可以在使用动作时创建一个新的动作类实例,但那样的话,每次使用动作时我都得遍历一个包含所有动作及其效果的数据库,这样效率似乎很低。因此,我在考虑为每个动作创建一个类,名称与文本框中显示的名称相同,这样我就可以更快地创建该特定动作的新实例。
3 个回答
与其生成 .py
文件,不如直接读取 csv 文件,然后进行 动态类型创建。这样,如果 csv 文件有变化,你可以确保你的类型会自动重新生成。
首先,你不需要把 Python 类分开放在不同的文件里。通常我们会根据功能把它们放在模块和包里(可以参考Python模块和包有什么区别?)。而且,200个相似的类听起来设计得有点奇怪——它们真的都需要吗?或者说,你能不能用字典来存储一些属性呢?
当然,你可以写一个简单的 Python 脚本,读取 CSV 文件,然后生成一个或多个包含类的 .py
文件(就是把文本写到文件里)。
这应该只需要几行代码,具体取决于你想要的定制程度。
如果这个列表有变化,你甚至不需要把类写到文件里:你可以直接动态生成它们。
如果你告诉我们你已经做到了什么,或者提供更多关于问题的细节,我们可以帮你完成代码……
正如其他人所说的,通常情况下,你会想要在运行时生成类,而不是创建单独的文件。
但我在想:如果你有很好的理由这样做,比如说只是为了给一堆文件制作类模板,这样你可以以后再去扩展它们呢?假设我打算写很多代码,所以我想自动化一些重复的代码部分,这样我就不需要做那些繁琐的工作了。
结果发现,为Python类写一个简单的模板引擎并不是那么难。这是我尝试的一个方法,它可以从csv文件中进行模板化。
from os import path
from sys import argv
import csv
INIT = 'def __init__'
def csvformat(csvpath):
""" Read a csv file containing a class name and attrs.
Returns a list of the form [['ClassName', {'attr':'val'}]].
"""
csv_lines = []
with open(csvpath) as f:
reader = csv.reader(f)
_ = [csv_lines.append(line)
for line in reader]
result = []
for line in csv_lines:
attr_dict = {}
attrs = line[1:]
last_attr = attrs[0]
for attr in attrs[1:]:
if last_attr:
attr_dict[last_attr] = attr
last_attr = ''
else:
last_attr = attr
result.append([line[0], attr_dict])
return result
def attr_template(attrs):
""" Format a list of default attribute setting code. """
attr_list = []
for attr, val in attrs.items():
attr_list.append(str.format(' if {} is None:\n', attr, val))
attr_list.append(str.format(' self.{} = {}\n', attr, val))
attr_list.append(' else:\n')
attr_list.append(str.format(' self.{} = {}\n', attr, attr))
return attr_list
def import_template(imports):
""" Import superclasses.
Assumes the .py files are named based on the lowercased class name.
"""
imp_lines = []
for imp in imports:
imp_lines.append(str.format('from {} import {}\n',
imp.lower(), imp))
return imp_lines
def init_template(attrs):
""" Template a series of optional arguments based on a dict of attrs.
"""
init_string = 'self'
for key in attrs:
init_string += str.format(', {}=None', key)
return init_string
def gen_code(foldername, superclass, name, attrs):
""" Generate python code in foldername.
Uses superclass for the superclass, name for the class name,
and attrs as a dict of {attr:val} for the generated class.
Writes to a file with lowercased name as the name of the class.
"""
imports = [superclass]
pathname = path.join(foldername, name.lower() + '.py')
with open(pathname, 'w') as pyfile:
_ = [pyfile.write(imp)
for imp
in import_template(imports)]
pyfile.write('\n')
pyfile.write((str.format('class {}({}):\n', name, superclass)))
pyfile.write((str.format(' {}({}):\n',
INIT, init_template(attrs))))
_ = [pyfile.write(attribute)
for attribute
in attr_template(attrs)]
pyfile.write(' super().__init__()')
def read_and_generate(csvpath, foldername, superclass):
class_info = csvformat(csvpath)
for line in class_info:
gen_code(foldername, superclass, *line)
def main():
read_and_generate(argv[1], argv[2], argv[3])
if __name__ == "__main__":
main()
上面的代码将一个格式如下的csv文件作为第一个参数(这里保存为a.csv
):
Magistrate,foo,42,fizz,'baz'
King,fizz,'baz'
其中第一个字段是类名,接下来是属性名及其默认值。第二个参数是输出文件夹的路径。
如果我创建一个名为classes
的文件夹,并在里面创建一个classes/mysuper.py
,里面有一个基本的类结构:
class MySuper():
def __init__(*args, **kwargs):
pass
然后像这样运行代码:
$ python3 codegen.py a.csv classes MySuper
我会得到文件classes/magistrate.py
,内容如下:
from mysuper import MySuper
class Magistrate(MySuper):
def __init__(self, fizz=None, foo=None):
if fizz is None:
self.fizz = 'baz'
else:
self.fizz = fizz
if foo is None:
self.foo = 42
else:
self.foo = foo
super().__init__()
还有classes/king.py
:
from mysuper import MySuper
class King(MySuper):
def __init__(self, fizz=None):
if fizz is None:
self.fizz = 'baz'
else:
self.fizz = fizz
super().__init__()
你实际上可以加载它们并使用它们哦!
$ cd classes
classes$ python3 -i magistrate.py
>>> m = Magistrate()
>>> m.foo
42
>>> m.fizz
'baz'
>>>
上面的代码生成的是Python 3的代码,这是我习惯的,所以如果你想在Python 2中使用,需要做一些小的修改。