3

I need to write dozen of class which differ each other only by variables, which are essential for stable work of class. Classes can not work without such variables and changing of variables can broke them. I have wriiten a code for creating a corresponding metaclss:

from abc import ABCMeta, abstractmethod, abstractproperty
import re

def get_field_protector(*names):
    def check_attributes(cls):
        for t in names:
            if not hasattr(cls, t):
                raise Exception('Abstract field "{0}" have to be defined'.format(t))
    def decorate_new_method(inp_method, class_name):
        def inner(cls, *args, **kwargs):
            if cls.__new__.decorated==class_name:
                check_attributes(cls)
            return inp_method(cls, *args, **kwargs)
        return inner
    class result_protector(ABCMeta):
        def __setattr__(self, name, value):
            if name in names:
                raise Exception("Read only class attribute!")
            super(self.__class__, self).__setattr__(name, value)
        def __delattr__(self, name):
            if name in names:
                raise Exception("Read only class attribute!")
            super(self.__class__, self).__delattr__(name)
        def __init__(self, name, bases, attributes):
            super(self.__class__, self).__init__(name, bases, attributes)
            if not hasattr(self.__new__, 'decorated'):
                self.__new__=decorate_new_method(self.__new__, name)
                self.__new__.decorated=name
    return result_protector 

It was work fine until I dstart use classes like:

class test(object, metaclass=get_field_protector("essential_field")):
    essential_field="some_value"
    def __init__(self, val):
        self.val=val

When I try to create such class, I receive:

>>> test(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/media/bykov/9016-4EF8/main_architecture.py", line 13, in inner
    return inp_method(cls, *args, **kwargs)
TypeError: object() takes no parameters
A. Bykov
  • 288
  • 1
  • 16
  • 3
    Never pass `type(self)` (or `self.__class__`, which is the same thing) to `super`! Your question is tagged `python-3.x`, so just use `super()` instead. See [Shortcut for super(type(self), self)](//stackoverflow.com/q/4883822). – Aran-Fey Aug 30 '18 at 06:35
  • 1
    Do you really need to be _that_ defensive ??? Python is a dynamic language and relies more on conventions _and_ proper documentation than on language-inforced restrictions, and trying to fight against Python's very nature is mostly a waste of time. – bruno desthuilliers Aug 30 '18 at 07:11
  • @brunodesthuilliers That wasn't supposed to be a duplicate. It doesn't solve the problem. – Aran-Fey Aug 30 '18 at 07:18
  • I don't understand why you're overwriting/decorating the class's `__new__` method. You understand that `__new__` is called every time you create an *instance* of the `test` class, right? Why do you need to call `check_attributes` every time a new instance is created? Can't you just call that once after the class has been created? – Aran-Fey Aug 30 '18 at 07:28
  • @Aran-Fey Maybe you are right. In case of redefining of __new__ method user have to do super().__new__(cls, *args, **kwargs) – A. Bykov Aug 30 '18 at 08:34
  • Take a look at this related question: [TypeError: object() takes no parameters after defining \_\_new\_\_](//stackoverflow.com/q/34777773) – Aran-Fey Aug 30 '18 at 09:35

1 Answers1

2

Bruno's comment is correct. In Python, you don't "protect" a property of a parent class, you simply name it with two prefixed underlines. In your example __essential_field. That means to leave it alone for any decendents.

C14L
  • 12,153
  • 4
  • 39
  • 52
  • The goal is not to make it "protected like C#" but to throw Exception then variable is occasionally changed or forgotten and is not defined. This realy save the time when debugging – A. Bykov Aug 30 '18 at 08:23