Как мы можем изменить атрибуты на основе атрибутов, переданных в init в метаклассе - PullRequest
0 голосов
/ 05 октября 2018

скажем, у меня есть класс, который принимает атрибут с именем bucket_name, на основе этого сегмента я хочу установить атрибут bucket_path для класса, который имеет вид bucket_path = "{bucket_name}_created".format(bucket_path='master-bucket')

Я пытаюсь использовать для него метаклассчто-то вроде этого:

class MyMeta(type):
    def __new__(meta, klassname, bases, attrs):
        # trying to get bucket_name here from attrs but attrs is {}
        return type.__new__(meta, klassname, bases, attrs)

class M(object):
    __metaclass__= MyMeta
    def __init__(self, bucket_name):
        self.bucket_name = bucket_name

но мне не удается, потому что attrs пуст, когда я делаю m = M ('my_bucket_name'), как это сделать?

1 Ответ

0 голосов
/ 12 октября 2018

Атрибут bucket_name является атрибутом instance , поэтому bucket_path не может зависеть от bucket_name и одновременно быть атрибутом класса.

Оба должны быть либо атрибутами класса, либо атрибутами экземпляра.

Атрибуты экземпляра

Если оба должны быть атрибутами экземпляра, тогда метакласс не нужен, достаточно property.

class M(object):
    def __init__(self, bucket_name):
        self.bucket_name = bucket_name

    @property
    def bucket_path(self):
        return "{}_created".format(self.bucket_name)

m = M('foo')
print(m.bucket_path) # foo_created

Атрибуты класса

Если оба должны быть атрибутами класса, то вы хотите определить свойство, но для класса.Хотя вы можете определить декоратор класса для реализации свойств класса , этого можно достичь и с помощью метакласса.

class MyMeta(type):
    def __new__(cls, name, bases, namespace):
        if 'bucket_name' not in namespace:
            raise TypeError("class must have 'bucket_name'")

        # This is meant to also delegate the instance attribute to the class property
        namespace['bucket_path'] = property(lambda self: type(self).bucket_path)

        return super(MyMeta, cls).__new__(cls, name, bases, namespace)

    @property
    def bucket_path(cls):
        return "{}_created".format(cls.bucket_name)

class M(object):
    __metaclass__ = MyMeta
    bucket_name = 'foo'

print(M.bucket_path) # foo_created
print(M().bucket_path) # foo_created
...