Reputation: 2673
say i have a class which takes a attribute called bucket_name, on basis of this bucket i want to set an attribute bucket_path for class which is of kind bucket_path = "{bucket_name}_created".format(bucket_path='master-bucket')
I am trying to use metaclass for it which is like this:
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
but i am failing because attrs is empty when i do m = M('my_bucket_name'), how to go about it?
Upvotes: 0
Views: 55
Reputation: 22324
The attribute bucket_name
is an instance attribute, thus it is not possible for bucket_path
to depend on bucket_name
and be a class attribute at the same time.
Both must either be class attributes or instance attributes.
If both are to be instance attributes, then a metaclass is unnecessary, a property
will suffice.
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
If both are to be class attributes, then you want to define a property, but on the class. While you could define a class decorator to implement class properties, this can indeed be achieved with a metaclass as well.
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
Upvotes: 1