避免在实例变量定义中使用exec

时间:2015-09-18 14:33:16

标签: python class namespaces

我有一个类,我发现自己多次定义相似的变量组,我相信如果我能够在循环中这样做更清楚,如下所示:

import tkinter as tk
from tkinter import ttk

class MyClass:
    def __init__(self):
        self.initializeVariables()

    def initializeVariables(self):
        varnames = ['a', 'b', 'c', 'd']
        for var in varnames:
            exec("self.bg{0} = ttk.Frame(self.Frame)".format(var))
            exec("self.tb{0} = ttk.Entry(bg{0}, width=4)".format(var))
            exec("self.tb{0}.grid(row=i)".format(var))

我有更多的元素,这个例子只是为了说明。我想知道:

  1. 有没有办法在不使用exec功能的情况下完成此操作?我试过globals(),但没有成功
  2. 这样做是不可取的?在我看来,这使代码更容易阅读,但我不知道这个领域的典型约定。

2 个答案:

答案 0 :(得分:3)

不需要为每个小部件设置单独的对象属性。在循环中创建属性会增加复杂性,而不会在这种情况下提供任何实际值。

我建议使用列表或字典来存储引用:

def initializeVariables(self):
    varnames = ['a', 'b', 'c', 'd']
    self.frames = {}
    self.entries = {}
    for var in varnames:
        self.frames[var] = ttk.Frame(...)
        self.entries[var] = ttk.Entry(...)

然后您可以通过他们的名字访问它们。例如:

self.entries["c"].get()

除非您需要访问代码其他部分的框架,否则可以使用框架的局部变量:

for var in varnames:
    frame = tk.Frame(...)
    self.entries[var] = ttk.Entry(frame, ...)

答案 1 :(得分:2)

您有几个选项远远优于exec

  • 在给定属性的字符串名称的情况下,使用setattr() function设置任意属性:

    varnames = ['a', 'b', 'c', 'd']
    for var in varnames:
        frame = ttk.Frame(self.Frame)
        entry = ttk.Entry(frame, width=4)
        entry.grid(row=i)
        setattr(self, 'bg{0}'.format(var), frame)
        setattr(self, 'tb{0}'.format(var), entry)
    
  • 使用vars(self)作为字典访问实例名称空间:

    namespace = vars(self)
    varnames = ['a', 'b', 'c', 'd']
    for var in varnames:
        frame = ttk.Frame(self.Frame)
        entry = ttk.Entry(frame, width=4)
        entry.grid(row=i)
        namespace['bg{0}'.format(var)] = frame
        namespace['tb{0}'.format(var)] = entry
    
  • 直接访问self.__dict__命名空间:

    varnames = ['a', 'b', 'c', 'd']
    for var in varnames:
        frame = ttk.Frame(self.Frame)
        entry = ttk.Entry(frame, width=4)
        entry.grid(row=i)
        self.__dict__['bg{0}'.format(var)] = frame
        self.__dict__['tb{0}'.format(var)] = entry
    

vars(self)基本上是一种前向兼容且API友好的拼写方式self.__dict__。我更喜欢使用setattr(),因为这与使用__slots__或挂钩__setattr__的类兼容。

在循环中设置属性很好(甚至很好,因为你应用DRY principle),但使用exec并不是最好的方法。

请考虑将字典或列表用于重复或分组的属性。字典或列表使得以后更容易访问go中的整个元素组:

frames = self.frames = {}
framed_entries = self.framed_entries = {}
varnames = ['a', 'b', 'c', 'd']
for var in varnames:
    frame = ttk.Frame(self.Frame)
    entry = ttk.Entry(frame, width=4)
    entry.grid(row=i)
    frames['bg{0}'.format(var)] = frame
    framed_entries['tb{0}'.format(var)] = entry

现在,您可以通过字典上的简单循环访问这些相同的框架和条目,或者使用合适的密钥直接对每个框架和条目进行寻址。