在python中构建类方法属性的dict

时间:2014-12-27 19:35:57

标签: python

我正在尝试将一个类中的函数组织成组。我决定使用设置属性值的记录为类中的每个函数添加一个属性。我希望有一个dict,也许在调用类时,键将是我设置的属性,值是方法。以下是代码示例:

def orientation(lr):
    def decorator(f):
        f.orientation = lr
        return f
    return decorator

def level(lr):
    def decorator(f):
        f.level = lr
        return f
    return decorator

class Artwork(object):
    def __init__(self):
        self.groups = {}

    def __call__(self):
        pass

    @level('good')
    @orientation(RIGHT)
    def build_one(self):
        return """ some stuff """

    @level('bad')
    @orientation(RIGHT)
    def build_two(self):
        return """ some stuff """

   @level('bad')
    @orientation(RIGHT)
    def build_three(self):
        return """ some stuff """

如果这样的话会被退回,那就太棒了:

test = Artwork()
test.groups 
>> {'good': [build_one], 'bad': [build_two, build_three]}

我尝试使用反射循环遍历类并使用hasattr查找属性,但我似乎无法使其正常工作。

更新:

我有另一个继承艺术品的艺术品。在Art类中,我想在一个'组中调用一个随机函数。目前我手动设置要使用的功能,但我试图通过拥有一组我可以根据一些初始条件随机调用的函数来使其更加健壮。

class Art(Artwork, object):

    """ Here is where instead of setting the available_art manually,
I will use the dict of groups of art functions.  
i.e. {'bad': [func1, func2], 'good': [func3, func5, func6]} """
def __init__(self, text, score):
    self.text = text
    self.available_art = [self.build_one,
                          self.build_two,
                          self.build_three,
                          self.build_four]
    self.score = score

def _decide_rank(self):
    total = functools.reduce(operator.add, self.score.values())
    passing = self.score.get('passing')
    percentage = (passing / total) * 100
    rank = SCORE_MAP[percentage] if percentage in SCORE_MAP else SCORE_MAP[min(SCORE_MAP.keys(), key=lambda k: abs(k-percentage))]
    return rank

def _get_longest_line(self, text):
    lines = text.split('\n')
    return len(max(lines, key=len))

def _build_bubble(self, text, spaces, orientation, length=40):
    bubble = []
    right = True if orientation == 'right' else False
    lines = self._normalize_text(text, length)
    bordersize = len(lines[0])

    rline = ' ' * spaces + '  ' + '-' * bordersize
    lline = '   ' + '-' * bordersize
    plines = rline if right else lline

    bubble.append(plines)

    for index, line in enumerate(lines):
        border = self._get_border(lines, index, spaces, right)
        bubble.append('%s %s %s' % (border[0], line, border[1]))

    bubble.append(plines)

    return '\n'.join(bubble)

def _normalize_text(self, text, length):
    lines  = textwrap.wrap(text, length)
    maxlen = len(max(lines, key=len))
    return [line.ljust(maxlen) for line in lines]

def _get_border(self, lines, index, spaces, right):
    if len(lines) < 2:
        return [' ' * spaces + '<', '>'] if right else [' ' + '<', '>']
    elif index == 0:
        return [' ' * spaces + '/', '\\'] if right else ['/', '\\']
    elif index == len(lines) - 1:
        return [' ' * spaces + '\\', '/'] if right else ['\\', '/']
    else:
        return [' ' * spaces + '|', '|'] if right else ['|', '|']

def _randomizer(self):
    return random.randrange(0, len(self.available_art))

def build(self):
    pic = self.available_art[self._randomizer()]
    spaces = self._get_longest_line(pic())
    orientation = pic.orientation
    print self._build_bubble(self.text, spaces, orientation) + pic()

1 个答案:

答案 0 :(得分:1)

您可以使用dir()迭代这些方法,如下所示:

Artwork.groups = {}
for attr in dir(Artwork):
    try:
        v = getattr(Artwork, attr).level
    except AttributeError:
        pass  # level-less
    else:
        Artwork.groups.setdefault(v, []).append(attr)

现在这两种形式都有效,因为在类级别设置groups(你只需要摆脱self.groups = {}行,因为它隐藏了类级成员):

Artwork.groups
=> {'bad': ['build_three', 'build_two'], 'good': ['build_one']}
Artwork().groups
=> {'bad': ['build_three', 'build_two'], 'good': ['build_one']}

使用dir比直接访问Artwork.__dict__更好,因为这也支持继承(假设你希望子类继承其超类的级别)

更新

如果您需要将其应用于多个类,请将其设为函数:

def add_groups(cls):
  cls.groups = {}
  for attr in dir(cls):
    try:
        v = getattr(cls, attr).level
    except AttributeError:
        pass  # level-less
    else:
        cls.groups.setdefault(v, []).append(attr)

然后应用它:

add_groups(Artwork)
add_groups(Art)

或者,如果您愿意,可以使用类装饰器。