[Python] Is it possible to modify a class with a decorator

Discussão em 'Python' iniciado por Stack, Abril 15, 2025.

  1. Stack

    Stack Membro Participativo

    I am writing a class in Python for some settings which looks like this:

    class _CanvasSettings:
    def __init__(self, **kwargs):
    super().__init__()
    self._size_x = _int(kwargs, 'size_x', 320)
    self._size_y = _int(kwargs, 'size_y', 240)
    self._lock_ratio = _bool(kwargs'lock_ratio', True)

    def get_size_x_var(self):
    return self._size_x
    def _get_size_x(self):
    return self._size_x.get()
    def _set_size_x(self, value):
    self._size_x.set(value)
    size_x = property(_get_size_x, _set_size_x)

    def get_size_y_var(self):
    return self._size_y
    def _get_size_y(self):
    return self._size_y.get()
    def _set_size_y(self, value):
    self._size_y.set(value)
    size_y = property(_get_size_y, _set_size_y)

    def get_lock_ratio_var(self):
    return self._lock_ratio
    def _get_lock_ratio(self):
    return self._lock_ratio.get()
    def _set_lock_ratio(self, value):
    self._lock_ratio.set(value)
    lock_ratio = property(_get_lock_ratio, _set_lock_ratio)


    as you can see I add the block:

    def get_something_var(self):
    return self._something
    def _get_something(self):
    return self._something.get()
    def _set_something(self, value):
    self._something.set(value)
    something = property(_get_something, _set_something)


    For every single setting.
    Is it possible to automate this task with a decorator?

    I would like to do it like this (pseudocode):

    def my_settings_class(cls):
    result = cls
    for field in cls:
    result.add_getter_setter_and_property( field )
    return result

    @my_settings_class
    class _CanvasSettings:
    def __init__(self, **kwargs):
    super().__init__()
    self._size_x = _int(kwargs, 'size_x', 320)
    self._size_y = _int(kwargs, 'size_y', 240)
    self._lock_ratio = _bool(kwargs'lock_ratio', True)

    # Done !


    Is this possible?
    If yes, how?
    How to implement the add_getter_setter_and_property() method?


    Edit:
    There is a pretty similar question here: How to decorate a class?
    from the answers there I suspect that it is possible to achive something like I have asked, but can you give me a clue on how I could implement the add_getter_setter_and_property() function/method?


    Note:
    the _int(), _bool() functions just return a tkinter Int/Bool-var eighter from the kwargs if the string (f.e. 'size_x') exist or from the default value (f.e. 320).


    My Final Solution: I think I have found a pretty good solution. I have to add a settings name only once, which in my opinion is awesome

    import tkinter as tk

    def _add_var_getter_property(cls, attr):
    """ this function is used in the settings_class decorator to add a
    getter for the tk-stringvar and a read/write property to the class.
    cls: is the class where the attributes are added.
    attr: is the name of the property and for the get_XYZ_var() method.
    """
    field = '_' + attr
    setattr(cls, 'get_{}_var'.format(attr), lambda self: getattr(self, field))
    setattr(cls, attr,
    property(lambda self: getattr(self, field).get(),
    lambda self, value: getattr(self, field).set(value)))

    def settings_class(cls):
    """ this is the decorator function for SettingsBase subclasses.
    it adds getters for the tk-stringvars and properties. it reads the
    names described in the class-variable _SETTINGS.
    """
    for name in cls._SETTINGS:
    _add_var_getter_property(cls, name)
    return cls


    class SettingsBase:
    """ this is the base class for a settings class. it automatically
    adds fields to the class described in the class variable _SETTINGS.
    when you subclass SettingsBase you should overwrite _SETTINGS.
    a minimal example could look like this:

    @settings_class
    class MySettings(SettingsBase):
    _SETTINGS = {
    'x': 42,
    'y': 23}

    this would result in a class with a _x tk-intvar and a _y tk-doublevar
    field with the getters get_x_var() and get_y_var() and the properties
    x and y.
    """

    _SETTINGS = {}

    def __init__(self, **kwargs):
    """ creates the fields described in _SETTINGS and initialize
    eighter from the kwargs or from the default values
    """
    super().__init__()
    fields = self._SETTINGS.copy()
    if kwargs:
    for key in kwargs:
    if key in fields:
    typ = type(fields[key])
    fields[key] = typ(kwargs[key])
    else:
    raise KeyError(key)
    for key in fields:
    value = fields[key]
    typ = type(value)
    name = '_' + key
    if typ is int:
    var = tk.IntVar()
    elif typ is str:
    var = tk.StringVar()
    elif typ is bool:
    var = tk.BooleanVar()
    elif typ is float:
    var = tk.DoubleVar()
    else:
    raise TypeError(typ)
    var.set(value)
    setattr(self, name, var)


    after that my settings classes simply look like this:

    @settings_class
    class _CanvasSettings(SettingsBase):

    _SETTINGS = {
    'size_x': 320,
    'size_y': 240,
    'lock_ratio': True
    }

    Continue reading...

Compartilhe esta Página