【问题标题】:How to use global variables created from string inside the same function?如何在同一个函数中使用从字符串创建的全局变量?
【发布时间】:2017-07-17 17:21:55
【问题描述】:

我正在尝试从 SPICE 网表文件中提取数据,特别是定义的参数。这是文件“netlist.sp”的(感兴趣的)内容:

.param freq = 860x powS = 0
    + pi = 3.141592
    + nper = 15 cap1 = 68p 
    + cycles = 20 
    + tper = '1/freq'       
    + tstep = 'tper/nper'
    + tstop = 'cycles*tper'

仅供参考,+ 符号表示继续上一行,param = 'equation' 计算表达式。

所以,我尝试在 Python 3.6 中为每个参数创建一个全局变量。这是我目前得到的代码:

def isnumber(s):
try:
    float(s)
    return True
except ValueError:
    return False

#This function is needed to convert the '68p' format to '68e-12'
def floatify(st):
    if not isnumber(st[-1]):
        vals = [ 't', 'g', 'x', 'meg', 'k', 'm',  'u',  'n',  'p',   'f',   'a']
        prod = [1e12, 1e9, 1e6,  1e6,  1e3, 1e-3, 1e-6, 1e-9, 1e-12, 1e-15, 1e-18]
        pos  = vals.index(st[-1])
        st = st[:-1]
        num = float(st) * prod[pos]
    else:
            num  = float(st)
    return num

#This is the main function
def params (file):
    fl = 0
    strng = '00'
    tnum  = 0.0
    with open(file) as dat:
        for line in dat:
            if line.startswith('*'):
                pass
            elif line.startswith('.param '):
                fl = 1
                spl = line.split()
                a = [i for i,x in enumerate(spl) if x=='=']
                for i in range(len(a)):
                    strng = spl[a[i]-1]
                    try:
                        tnum = floatify(spl[a[i]+1])
                    except ValueError:
                        tnum = eval(spl[a[i]+1])
                    globals()[strng] = tnum
            elif (line.find('+')+1) and fl:
                spl = line.split()
                a = [i for i,x in enumerate(spl) if x=='=']
                for i in range(len(a)):
                    strng = spl[a[i]-1]
                    try:
                        tnum = floatify(spl[a[i]+1])
                    except ValueError:
                        temp = spl[a[i]+1]
                        tnum = eval(temp)
                    globals()[strng] = tnum
            elif (not (line.find('+')+1)) and fl:
                break

params('netlist.sp')

#Testing the variables
print('params done')
print('freq = ', freq)
print('powS = ', powS)
print('pi = ', pi)
print('nper = ', nper)
print('cap1 = ', cap1)
print('cycles = ', cycles)
print('tper = ', tper)
print('tstep = ', tstep)
print('tstop = ', tstop)

# Testing the eval function:
print(eval('1/freq'))
print(eval('2*pi'))

globals()[strng] = tnum 语句从提取的字符串创建全局变量并分配相应的值。

输出是:

freq =  860000000.0
powS =  0.0
pi =  3.141592
nper =  15.0
cap1 =  6.8e-11
cycles =  20.0
tper =  1/freq
tstep =  tper/nper
tstop =  cycles*tper
1.1627906976744186e-09
6.283184

所以,我从eval 函数的测试中了解到,在params 函数内部创建的全局变量只能在函数本身之外被理解。我知道要修改函数内的全局变量,您必须在函数内声明 global var 语句。我的问题是在这种情况下动态创建变量时如何做到这一点?

【问题讨论】:

  • 为什么不直接创建一个params = {} 字典,然后用你想要的任何内容填充它,然后使用global params 在函数中访问它。
  • 是的,我考虑过使用字典,但是使用eval 函数来提取表达式定义的参数值会变得更加复杂。
  • 您不必评估它们,这就是重点。通过查看您的代码,您使整个事情变得过于复杂。
  • 这似乎过于复杂了,因为这是一个非常简单的案例,即使使用数学函数param3 = sin(param1)+exp(param2)也可以定义参数。所以我认为eval 函数是最好的选择,除非你知道更简单的字典方法。请记住,我首先得到了一个字符串表达式,而替代方法是将字符串表达式解析为 python 方程格式,这正是 eval 函数所做的。

标签: python python-3.x global-variables


【解决方案1】:

注意:repl.it 可以清理很多,但它确实适用于示例数据。

正如repl.it 所示,您可以使用字典轻松完成此操作。

def fill_param(token):
    for key in params.keys():
        token = token.replace(key, str(params[key]))
    return token 

是允许这样做的关键:它使用 str.replace 来填充我们在eval它之前已经知道的任何东西的值:

params[key] = eval(fill_param(value))

process_params() 的开头也很有趣:

global params
tokens = shlex.split(line)[1:]

我们导入字典,然后使用shlex.split() 对字符串进行标记,省略第一个标记(.param+,具体取决于行)。 shlex.split() 很好,因为它尊重引用。


完整代码(以防 repl.it 死掉)。请注意,它还有很多不足之处,因为我已经花在这个问题上了。我把清理工作留给读者作为练习。

import shlex

with open("netlist.sp", "w") as f:
    f.write("""cabbage
garbage
.param freq = 860x powS = 0
+ pi = 3.141592
+ nper = 15 cap1 = 68p 
+ cycles = 20 
+ tper = '1/freq'       
+ tstep = 'tper/nper'
+ tstop = 'cycles*tper'
sweet american freedom""")

params = {}
def param_it(in_f):

    def isnumber(s):
        try:
            float(s)
            return True
        except ValueError:
            return False

    def floatify(st):
        if not isnumber(st):
            vals = [ 't', 'g', 'x', 'meg', 'k', 'm',  'u',  'n',  'p',   'f',   'a']
            prod = [1e12, 1e9, 1e6,  1e6,  1e3, 1e-3, 1e-6, 1e-9, 1e-12, 1e-15, 1e-18]
            pos  = vals.index(st[-1])
            st = st[:-1]
            num = float(st) * prod[pos]
        else:
                num  = float(st)
        return num

    def number(st):
        if isnumber(st) or len(st) == 1 and st in '0123456789':
            return True
        return st[-1] not in '0123456789' and isnumber(st[:-1])

    def process_params(line):
        global params
        tokens = shlex.split(line)[1:]
        assert len(tokens) % 3 == 0
        for i in range(len(tokens)/3):
            key = tokens[i*3]
            value = tokens[i*3 + 2]
            print "Processing key: %s value: %s... " % (key, value),

            if number(value):
                try:
                    value = float(value)
                except ValueError:
                    value = floatify(value)
                params[key] = value
            else:
                try:
                    params[key] = eval(fill_param(value))
                except Exception as e:  # eval can throw essentially any exception
                    print "Failed to parse value for k/v %s:%s" % (key, value)
                    raise
            print "Converted value is : %s\n" % params[key]

    def fill_param(token):
        for key in params.keys():
            token = token.replace(key, str(params[key]))
        return token

    with open(in_f, "r") as f:
        param = False
        for line in f:
            if line.startswith(".param "):
                process_params(line)
                param = True
            elif param and line.startswith("+"):
                process_params(line)
            elif param:
                break

param_it("netlist.sp")

print params

【讨论】:

  • 是的,这就是我想要的。对不起,如果我听起来很固执,我不知道 shlex 包。非常感谢您的帮助。
  • @SalatielGarcia shlex 用于命令行处理,但在这种情况下,我认为它对你有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-01
  • 2020-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多