制作一个可以在lambda中使用的属性的最pythonic方法是什么?

时间:2011-07-22 17:43:58

标签: oop tkinter python

更具体地说,我希望能够支持lambda: <some_or_other_setter>,但我想保持代码清晰,简洁。我必须验证值,所以我需要一个某种类型的setter。我需要使用lambda,因为我需要将回调传递给Tkinter事件。我还需要能够修改绑定之外的属性值。

在以下示例中,假设已全局声明了名为spam_button的按钮窗口小部件。还假设类Eggs将具有至少10个属性,而不是以相同的方式访问所有属性(我喜欢一致性)。

我可以做的第一种可能的方法是使用getter和setter:

class Eggs(object):

    def __init__(self):
        self._spam = ''
        self.set_spam('Monster')
        print self.get_spam()
        spam_button.bind('<Enter>', lambda: self.set_spam('Ouch'))

    def set_spam(self, spam):
        if len(spam) <= 32:
            self._spam = spam

    def get_spam(self):
        return self._spam

但是如果我使用这种方法并且有很多属性,代码可能会花太长时间来管理。

我能做到的第二种方法是使用属性,并在回调中使用setter:

class Eggs(object):

    def __init__(self):
        self._spam = ''
        self.spam = 'Monster'
        print self.spam
        spam_button.bind('<Enter>', lambda: self.set_spam('Ouch'))

    def set_spam(self, spam):
        if len(spam) <= 32:
            self._spam = spam

    def get_spam(self):
        return self._spam

    spam = property(get_spam, set_spam)

这种方式比直接访问属性时的方式简单,但在使用lambda时不一致。

第三种方法是使用get和set方法创建一个额外的类:

class Spam(object):

    def __init__(self):
        self._value = ''

    def set(self, value):
        if len(spam) <= 32:
            self._value = value

    def get(self):
        return self._value


class Eggs(object):

    def __init__(self):
        self._spam = ''
        self.spam = Spam()
        self.spam.set('Monster')
        print self.spam.get()
        spam_button.bind('<Enter>', lambda: self.spam.set('Ouch'))

这种方法比第一种方法更有条理,但我需要为每种类型的验证创建一个类。

我可能采用的最后一种方法是使用方法而不是属性(属性是第二个例子):

class Eggs(object):

    def __init__(self):
        self._spam = ''
        self.spam('Monster')
        print self.spam()
        spam_button.bind('<Enter>', lambda: self.spam('Ouch'))

    def spam(self, spam=None):
        if spam != None: 
            if len(spam) <= 32:
                self._spam = spam
        else:
            return self._spam

这种方法可能是最短的,但是一眼就能看出我是否正在接受或设置。

首选哪种(如果有)这些方法?

4 个答案:

答案 0 :(得分:2)

使用闭包返回要用作回调的函数,并将每个条件定义为函数(使用lambdadef,您的偏好)而不是每个 setter 作为一个函数。

class Eggs(object):

    def __init__(self):
        self.spam = 'Monster'
        def spam_condition(string = None):
            return (string is not None) and (len(string) <= 32)
        self.spam_setter = self.set('spam', 'Ouch', spam_condition)
        spam_button.bind('<Enter>', self.spam_setter)
        self.spam_setter(val='Horse')


    def set(self, name, value, condition):
        def setter(val):
            if type(val) is not .... :  # fill this in with the TYPE of the event
                value = val
            if condition(value):
                setattr(self, name, value)
        return setter

编辑:现在你可以从任何地方调用setter来检查条件。

编辑2:这样,setter将检查它是否有事件,如果没有,则使用它传递的值。你也可以使用:

if not isinstance(val, ....) :  # fill this in with the CLASS of the event

答案 1 :(得分:1)

我仍然建议使用dict并将其称为一天。对它进行子类化并覆盖__setitem__以进行数据验证,然后不要担心getter:

#!/usr/bin/env python

class Egg(dict):
    def __init__(self, *args, **kwargs):
        super(Egg, self).__init__(*args, **kwargs)
        self['spam'] = 'foo'
        spam_button.bind('<Enter>', lambda: self.__setitem__('spam', 'Ouch'))

    def __setitem__(self, key, value):
        if key == 'spam':
            if len(value) > 32:
                raise ValueError('"%s" is longer than 32 characters')
            return super(Egg, self).__setitem__(key, value)
        raise KeyError(key)

答案 2 :(得分:0)

可以使用property

处理验证问题
class Egg(object):
    @property
    def spam(self):
        return self._spam

    @spam.setter
    def spam(self, value):    
        if len(value) <= 32:
            self._spam = value

显然,你仍然可以使用self._spam = 'spam '*10+'baked beans and spam'而不受惩罚。

使用内置setattr

lambda: setattr(self, 'spam', 'Ouch')

如果您反对..."spam"...而只喜欢...spam...,则可以使用property的方法:

lambda: self.__class__.spam.fset(self, 'Ouch')

或者,因为property是一个描述符:

lambda: type(self).spam.__set__(self, 'Ouch')

但第一个版本是首选,我希望有明显的理由。

答案 3 :(得分:0)

我喜欢基于类的方法。您可能需要进行一组有限的验证(例如字符串的最大长度,数字的有效范围等),因此您将拥有有限数量的类。

例如:

class LimitedLengthString(object):
    def __init__(self, max_length):
        self.max_length = max_length
    def set(self, value):
        if len(value) <= self.max_length:
            self.value = value
    def get(self):
        return value

class Eggs(object):
    def __init__(self):
        self.spam = LimitedLengthString(32)
        self.spam.set('Monster')
        print self.spam.get()
        spam_button.bind('<Enter>', lambda: self.spam.set('Ouch'))

如果您确实需要对不同的属性进行唯一的验证,那么您就无法编写大量代码。但是一如既往地在编程中,在你开始重复自己的时候进行概括。


在TokenMacGuy的建议之后更新:替代使用相当未知的Python功能“描述符”:

class LimitedLengthString(object):
    def __init__(self, name, max_length):
        self.name = name
        self.max_length = max_length

    def __set__(self, instance, value):
        if len(value) <= self.max_length:
            instance.__dict__[self.name] = value

    def __get__(self, instance, owner):
        return instance.__dict__[self.name]

class Eggs(object):
    spam = LimitedLengthString('spam', 32)
    def __init__(self):
        self.spam = 'Monster'
        print self.spam  # prints 'Monster'
        self.spam = 'x' * 40
        print self.spam  # still 'Monster'
        spam_button.bind('<Enter>', lambda: self.spam = 'Ouch')

我找到了pretty good introduction to descriptors