简体   繁体   English

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

[英]Create variables with specific type on the fly in Python

while reading a text file with values, i want to create variables on the fly.在读取带有值的文本文件时,我想动态创建变量。

I've got the values stored in a list of lists:我将值存储在列表列表中:

values = list(content[startline].split() for i in range(_n_lines))

content is a list of the lines content是行的列表

The variable names are stored in a tuple of tuples, depending on the block I'm reading:变量名称存储在元组的元组中,具体取决于我正在阅读的块:

variable_names = (
    ('_idx_brg', 'stn', 'stn_rel', '_n_lines', '_brg_type'),
    ('D', 'L', 'Cb', 'visc'),
    ('stiff', 'damp'),
    ('damp_model', ''))

By default I convert the values into float:默认情况下,我将值转换为浮点数:

for irow,row in enumerate(variable_names):
    for icol,col in enumerate(row):
        if col:
            val = float(values[irow][icol])
            setattr(self, col, val)

Here is my issue:这是我的问题:

In some cases i need a different type and I want to avoid another list of lists.在某些情况下,我需要不同的类型,并且我想避免使用另一个列表。 Is there a clean and short way to provide a type for each variable?有没有一种简洁的方法来为每个变量提供一个类型? I thought about putting the info into variable_names , but that just seems wrong to me.我想过将信息放入variable_names ,但这对我来说似乎是错误的。

I would be glad for any advices.我很乐意提供任何建议。 Also for the part that I'm already using.也适用于我已经在使用的部分。

*edit @Rory *编辑@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.如您所见,我并不总是阅读整个区块。

Well, without getting into the details of your nesting, you could attach a variable type to the name by using a tuple.好吧,无需深入了解嵌套的细节,您可以使用元组将变量类型附加到名称。

I've done this on 2 of your variable names: ('_idx_brg',str), ('stn','int')我已经对你的两个变量名做了这个: ('_idx_brg',str), ('stn','int')

Rather than using zip , you'll need to hook that back up to your nested tuples and you'll also need to add error handling in case the string value from the file doesn't fit the expected variable type.而不是使用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))

output: output:



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}
                   👆           👆

You could even build your own classes.你甚至可以建立自己的课程。

class Myclass:
   def __init__(self, value):
      self.value = value

#part of your name/type tuples...
(('somevar', Myclass), ('_idx_brg',str)...)

edit re.编辑重新。 yaml: yaml:

I am not testing this so you may have to adjust a bit, esp around the exact yaml to get a dict with a nested varnames dict in it.我没有对此进行测试,因此您可能需要进行一些调整,尤其是围绕确切的 yaml 以获得其中包含嵌套varnames dict 的 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))

Now, this does rely on all instances of a given variable name having the same type no matter where in the data hierarchy, but that's just best practices.现在,这确实依赖于具有相同类型的给定变量名称的所有实例,无论数据层次结构中的哪个位置,但这只是最佳实践。

The other thing is that, if I have one area that looks a bit fishy/brittle to me, it is your complex nesting with tuples of values and names that somehow need to be always in synch.另一件事是,如果我有一个区域对我来说看起来有点可疑/脆弱,那就是您的复杂嵌套与值和名称的元组,不知何故需要始终保持同步。 Much more so than your basic requirement to load text data (whose format is not under your control) but then format it different ways.比您加载文本数据(其格式不受您控制)的基本要求要多得多,然后以不同的方式对其进行格式化。 I'd work at getting your names to flow more naturally with the data, somehow.不知何故,我会努力让你的名字更自然地与数据一起流动。 Maybe try to identify incoming data by record types and then assign a mapping class to it?也许尝试通过记录类型识别传入数据,然后为其分配映射 class ? Same thing as what you're doing, really, just not relying on complex nesting.实际上,与您正在做的事情相同,只是不依赖于复杂的嵌套。

Or maybe, going from your remark about row, column, you could put all that into the yaml config file as well, load that into a mapping data structure and explicitly use indices rather than nested loops?或者,从您关于行、列的评论来看,您也可以将所有这些都放入 yaml 配置文件中,将其加载到映射数据结构中并显式使用索引而不是嵌套循环? Might make your code a lot simpler to reason about and adjust for data changes.可能会使您的代码更易于推理和调整数据更改。

There are also interesting things in the Python data parsing space like Pydantic . Python 数据解析空间中也有一些有趣的东西,比如Pydantic Might or might not be helpful.可能有帮助,也可能没有帮助。

From your last paragraph I get the impression that you can control the file format.从你的最后一段我得到的印象是你可以控制文件格式。 That being the case, I'd suggest you consider yaml.既然如此,我建议你考虑 yaml。

With YAML, as well as numbers, strings, arrays and objects, yaml supports custom classes.通过 YAML 以及数字、字符串、arrays 和对象,yaml 支持自定义类。

The following would indicate that you want thing to be a Meh object.以下将表明您希望事物成为 Meh object。 Check out the pyyaml docs for more detail.查看 pyyaml 文档以获取更多详细信息。

thing: !Meh
    foo: bar
    ping: echo

I also get the impression that you're essentially writing your own parser for your own format.我还觉得您实际上是在为自己的格式编写自己的解析器。 It's generally better to use a battle hardened off the shelf parser, with a battle hardened proven format.通常最好使用经过实战考验的现成解析器,并具有经过实战验证的格式。 It's one less avenue for bugs, and you can stand on the shoulders of the giants who wrote the parser, and fixed any bugs that were found over the years.这是减少错误的一条途径,您可以站在编写解析器的巨人的肩膀上,并修复多年来发现的任何错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM