在Python中动态创建具有特定类型的变量

2024-05-01 23:51:35 发布

您现在位置: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里,但我觉得这是不对的

我很乐意接受任何建议。同样对于我已经使用的部分

*edit @Rory

Here is a sample input text block for the stated example

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

The input file has several blocks like this and of course some with another format. The identification of the blocks is done elsewhere in the script.

As you can see, I don't always read the whole block.


Tags: thein列表fornames情况colcontent
2条回答

从你的最后一段我得到的印象是,你可以控制文件格式。既然如此,我建议你考虑一下YAML。

对于YAML以及数字、字符串、数组和对象,YAML支持自定义类

以下内容表明您希望对象是Meh对象。查看pyyaml文档了解更多详细信息

thing: !Meh
    foo: bar
    ping: echo

我还得到这样的印象:您实际上是在为自己的格式编写自己的解析器。一般来说,最好使用经过战斗考验的现成解析器,以及经过战斗考验的成熟格式。这是减少bug的一条途径,您可以站在编写解析器的巨人的肩膀上,修复多年来发现的任何bug

好的,不必深入了解嵌套的细节,您可以通过使用元组将变量类型附加到名称

我已经在您的两个变量名上完成了此操作:('_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周围,以获得一个包含嵌套的varnamesdict的dict

 -
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。可能有用,也可能没用

相关问题 更多 >