python - 在 Python 中动态创建具有特定类型的变量
问题描述
在读取带有值的文本文件时,我想动态创建变量。
我将值存储在列表列表中:
values = list(content[startline].split() for i in range(_n_lines))
content
是行列表
变量名称存储在元组的元组中,具体取决于我正在阅读的块:
variable_names = (
('_idx_brg', 'stn', 'stn_rel', '_n_lines', '_brg_type'),
('D', 'L', 'Cb', 'visc'),
('stiff', 'damp'),
('damp_model', ''))
默认情况下,我将值转换为浮点数:
for irow,row in enumerate(variable_names):
for icol,col in enumerate(row):
if col:
val = float(values[irow][icol])
setattr(self, col, val)
这是我的问题:
在某些情况下,我需要不同的类型,并且我想避免使用另一个列表。有没有一种简洁的方法来为每个变量提供一个类型?我想过将信息放入variable_names
,但这对我来说似乎是错误的。
我很乐意提供任何建议。也适用于我已经在使用的部分。
*编辑@Rory
这是所述示例的示例输入文本块
6 28 0 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Some comments here 12.527 4.6 0.0365 3.5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
输入文件有几个像这样的块,当然还有一些具有另一种格式。块的识别在脚本的其他地方完成。
如您所见,我并不总是阅读整个区块。
解决方案
好吧,无需深入了解嵌套的细节,您可以使用元组将变量类型附加到名称。
我在你的两个变量名上做了这个:('_idx_brg',str), ('stn','int')
而不是使用zip
,您需要将它连接到您的嵌套元组,并且您还需要添加错误处理,以防文件中的字符串值不符合预期的变量类型。
import builtins
import pdb
def set_attr(tgt, names, values):
try:
for name, value in zip(names, values):
cls_ = None
if isinstance(name, str):
setattr(tgt, name, float(value))
elif isinstance(name, tuple):
name, cls_ = name
if callable(cls_):
setattr(tgt, name, cls_(value))
elif isinstance(cls_, str):
cls_ = globals().get(cls_) or getattr(builtins, cls_)
setattr(tgt, name, cls_(value))
else:
raise ValueError("variable types have to be a string or callable like `int`,`float`, etc")
except (ValueError,TypeError,AttributeError) as e:
print(f" somethings wrong:\n{dict(exception=e, name=name, cls_=cls_, value=value)}")
#raise
#pragma: no cover pylint: disable=unused-variable
except (Exception,) as e:
if 1:
pdb.set_trace()
raise
class Foo:
pass
variable_names = ('_idx_brg', 'stn', 'stn_rel', '_n_lines', '_brg_type')
values = (1.0, 1, 1.2, 1.3, 1.4, 1.5)
foo = Foo()
print("\n\nsetting for foo")
set_attr(foo, variable_names, values)
print("\n\nfoo:", vars(foo))
variable_names2 = (('_idx_brg',str), ('stn','int'), 'stn_rel', '_n_lines', ('_brg_type','xxx'))
bar = Foo()
print("\n\nsetting for bar:")
set_attr(bar, variable_names2, values)
print("\n\nbar:", vars(bar))
输出:
setting for foo
foo: {'_idx_brg': 1.0, 'stn': 1.0, 'stn_rel': 1.2, '_n_lines': 1.3, '_brg_type': 1.4}
setting for bar:
somethings wrong:
{'exception': AttributeError("module 'builtins' has no attribute 'xxx'"), 'name': '_brg_type', 'cls_': 'xxx', 'value': 1.4}
bar: {'_idx_brg': '1.0', 'stn': 1, 'stn_rel': 1.2, '_n_lines': 1.3}
你甚至可以建立自己的课程。
class Myclass:
def __init__(self, value):
self.value = value
#part of your name/type tuples...
(('somevar', Myclass), ('_idx_brg',str)...)
编辑重新。yaml:
我没有对此进行测试,因此您可能需要进行一些调整,尤其是围绕确切的 yaml 来获得包含嵌套varnames
字典的字典。
---
varnames:
_idx_brg: str
stn : int
from yaml import safe_load as yload
with open("myconfig.yaml") as fi:
config = yload(li)
mapping = {}
#the yaml is all strings right now
# map it to actual types/classes
for name, type_ in config["varnames"].items():
cls_ = globals().get(type_) or getattr(builtins, type_)
mapping[name] = cls_
#using it
for name, value in zip(names, values):
#fall back to `float` if there is no special-case for this varname
cls_ = mapping.get(name, float)
setattr(tgt, name, cls_(value))
现在,这确实依赖于具有相同类型的给定变量名称的所有实例,无论数据层次结构中的哪个位置,但这只是最佳实践。
另一件事是,如果我有一个区域对我来说看起来有点可疑/脆弱,那就是您的复杂嵌套与值和名称的元组,不知何故需要始终保持同步。比您加载文本数据(其格式不受您控制)的基本要求要多得多,然后以不同的方式对其进行格式化。不知何故,我会努力让你的名字更自然地与数据一起流动。也许尝试通过记录类型识别传入数据,然后为其分配一个映射类?实际上,与您正在做的事情相同,只是不依赖于复杂的嵌套。
或者,也许,从您关于行、列的评论开始,您也可以将所有这些都放入 yaml 配置文件中,将其加载到映射数据结构中并显式使用索引而不是嵌套循环?可能会使您的代码更易于推理和调整数据更改。
Python 数据解析领域也有一些有趣的东西,比如Pydantic。可能有帮助,也可能没有帮助。
推荐阅读
- r - 从另一个数据框 r 中添加/减去数据框行
- android - 使用相同布局中的过渡动画布局更改
- python - 我的脚本没有提取所有广告详细信息,而且我没有得到正确的数字
- javascript - 使用“chrome.webNavigation.onCompleted.addListener”时获取“”Uncaught TypeError:无法添加侦听器“
- android - Recyclerview 不显示警报对话框中的数据列表
- ajax - 块上传不适用于大文件 - Laravel 5.8
- aws-lambda - 如何从 Postman 调用受授权人保护的 Lambda?
- powershell - 如何使用 PSM1 文件安装模块?
- sql-server - 在 SQL JOB 中运行 BCP 命令时出错
- angularjs - 如何在 Angular JS 中使用 gridOption-enableRowselection 动态更改 ui-grid