Подтвердить что ты не робот

Python: универсальные получатели и сеттеры

TL; DR: необходимо определить уникальный набор геттеров и сеттеров для каждого свойства() 'd variable sucks. Могу ли я определить общие геттеры и сеттеры и использовать их с любой переменной, которую я хочу?

Скажем, я делаю класс с некоторыми хорошими геттерами и сеттерами:

class Foo
    def getter(self):
        return _bar+' sasquatch'

    def setter(self, value):
        _bar = value+' unicorns'

    bar = property(getter, setter)

Довольно здорово, правда?

Теперь позвольте сказать, что я ввел другую переменную под названием "baz", и я не хочу, чтобы ее не оставляли в стороне от этой забавы sasquatch/unicorn. Ну, я думаю, я мог бы сделать еще один набор геттеров и сеттеров:

class Foo
    def bar_getter(self):
        return _bar+' sasquatch'

    def bar_setter(self, value):
        _bar = value+' unicorns'

    bar = property(bar_getter, bar_setter)

    def baz_getter(self):
        return _baz+' sasquatch'

    def baz_setter(self, value):
        _baz = value+' unicorns'

    baz = property(baz_getter, baz_setter)

Но это не очень СУХОЙ и бесполезно загромождает мой код. Думаю, я мог бы сделать это немного DRYer:

class Foo
    def unicornify(self, value):
        return value+' unicorns'

    def sasquatchify(self, value):
        return value+' sasquatch'

    def bar_getter(self):
        return self.sasquatchify(_bar)

    def bar_setter(self, value):
        _bar = self.unicornify(_bar)

    bar = property(bar_getter, bar_setter)

    def baz_getter(self):
        return self.sasquatchify(_baz)

    def baz_setter(self, value):
        _baz = self.unicornify(_baz)

    baz = property(baz_getter, baz_setter)

Хотя это может сделать мой код DRYer, он не идеален. Если бы я хотел unicornify и sasquatchify еще две переменные, мне пришлось бы добавить еще четыре функции!

Должен быть лучший способ сделать это. Могу ли я использовать один общий приемник и/или сеттер для нескольких переменных?

Без единой и безсмысленной реализации реального мира: Я использую ORM SQLAlchemy и хочу преобразовать некоторые данные при их хранении и извлечении из базы данных. Некоторые из преобразований применимы к нескольким переменным, и я не хочу загромождать свои классы с помощью геттеров и сеттеров.

4b9b3361

Ответ 1

Как насчет просто:

def sasquatchicorn(name):
    return property(lambda self: getattr(self, name) + ' sasquatch',
                    lambda self, val: setattr(self, name, val + ' unicorns'))

class Foo(object):
    bar = sasquatchicorn('_bar')
    baz = sasquatchicorn('_baz')

Несколько более общий:

def sasquatchify(val):
    return val + ' sasquatch'

def unicornify(val):
    return val + ' unicorns'

def getset(name, getting, setting):
    return property(lambda self: getting(getattr(self, name)),
                    lambda self, val: setattr(self, name, setting(val)))

class Foo(object):
    bar = getset('_bar', sasquatchify, unicornify)
    baz = getset('_baz', sasquatchify, unicornify)

Или, если вы работаете не так, вы можете использовать полный протокол дескриптора, как описано в agf answer.

Ответ 2

Это то, что протокол дескриптора property основан на:

class Sasicorn(object):                              
    def __init__(self, attr):                        
        self.attr = "_" + attr                       
    def __get__(self, obj, objtype):                 
        return getattr(obj, self.attr) + ' sasquatch'
    def __set__(self, obj, value):                   
        setattr(obj, self.attr, value + ' unicorns') 

class Foo(object):                                   
    def __init__(self, value = "bar"):               
        self.bar = value                             
        self.baz = "baz"                             
    bar = Sasicorn('bar')                            
    baz = Sasicorn('baz')                            

foo = Foo()                                          
foo2 = Foo('other')                                  
print foo.bar                                        
# prints bar unicorns sasquatch
print foo.baz                                        
# prints baz unicorns sasquatch
print foo2.bar                                       
# prints other unicorns sasquatch

Хотя функция property в функции factory может быть прекрасной для вашего примера с игрушкой, это звучит, как будто вам нужно больше контроля для вашего реального использования.

Ответ 3

Один из моих коллег предложил использовать закрытие для возврата функций getter и setter, которые я решил использовать.

class Foo(object):
    def setter(var):
        def set(self, value):
            setattr(self, var, value+' unicorn')
        return set

    def getter(var):
        def get(self):
            return getattr(self, var)+' sasquatch'
        return get

    bar = property(getter('_bar'), setter('_bar'))

f = Foo()
f.foo = 'hi'
print f.foo

Но спасибо всем за ваши ответы:)

Ответ 4

Используя getattribute и setattr, вы может определить это для всех атрибутов прошлого и будущего.

class Foo(object):                                                                                                                           

  x = 3 

  def __getattribute__(self, attr):
    return str(object.__getattribute__(self, attr)) + ' sasquatch'

  def __setattr__(self, attr, value):
    object.__setattr__(self, attr, str(value) + ' unicorn')

print Foo.x
f = Foo()
print f.x 
f.y = 4 
print f.y 

Отпечатки:

3
3 sasquatch
4 unicorn sasquatch

Ответ 5

# coding=utf-8
__author__ = 'Ahmed Şeref GÜNEYSU'


class Student(object):
    def __init__(self, **kwargs):
        for k, v in kwargs.iteritems():
            self.__setattr__(k, v)

if __name__ == '__main__':
    o = Student(first_name='Ahmed Şeref', last_name='GÜNEYSU')
    print "{0} {1}".format(o.first_name, o.last_name)
    print o.email

Дает

Ahmed Şeref GÜNEYSU
  File "/Users/ahmed/PycharmProjects/sanbox/abstract_classes/object_initializer/__init__.py", line 13, in <module>
    print o.email
AttributeError: 'Student' object has no attribute 'email'

Process finished with exit code 137