In the following code, I want metaclass NameMeta to add attribute gender to MyName class in case this class does not declare that attribute.
class NameMeta(type):
def __new__(cls, name, bases, dic):
if 'gender' not in dic:
setattr(name, 'gender', 'Male')
return super().__new__(cls, name, bases, dic)
class MyName(metaclass=NameMeta):
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
def fullname(self):
self.full_name = self.fname + self.lname
return self.full_name
inst = MyName('Joseph ', 'Vincent')
print(MyName.gender)
This is the output that I am getting:
<ipython-input-111-550ff3cfae41> in __new__(cls, name, bases, dic)
2 def __new__(cls, name, bases, dic):
3 if 'gender' not in dic:
----> 4 setattr(name, 'gender', 'Male')
5 return super().__new__(cls, name, bases, dic)
6
AttributeError: 'str' object has no attribute 'gender'
I know this error makes sense since name is a string.
My question is, how can I access MyName class as an object in the metaclass so that I can add the attribute?
You were close. Your problem is that you were trying to add your attribute to the name of the meta-class using name, which is a string. You need to assign the attribute to the class object you're creating. This can be done using dic:
class NameMeta(type):
def __new__(cls, name, bases, dic):
if 'gender' not in dic:
dic['gender'] = 'Male'
return super().__new__(cls, name, bases, dic)
With the above change your code outputs:
Male
You can just add it to the dic if it is not present, as it holds the class's attribute:
def __new__(mcs, name, bases, dict):
if 'gender' not in dict:
dict['gender'] = 'Male'
# or just `dict.setdefault('gender', 'Male')`
return super().__new__(mcs, name, bases, dic)
# Or you can create the class and set it
cls = super().__new__(mcs, name, bases, dic)
if not hasattr(cls, 'gender'):
cls.gender = 'Male'
return cls
Or you could have a class attribute:
class NameMeta(type):
gender = 'Male'
# `gender = 'Male'` will be inherited by all classes
# but not instances of those classes.
Related
Im new to oop with python. Why am I getting this error? Shouldnt it print tom and 12?
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def name(self):
return self.name
def age(self):
return self.age
dog = Dog("tom", 12)
print(dog.name())
print(dog.age())
Instance attributes take precedence over class attributes when one of each exists and they have the same name. If you are going to have a method that returns the value of an attribute, a common convention is to make the name of the instance attribute a "private" version of the method name by prefixing an underscore to the name.
class Dog:
def __init__(self, name, age):
self._name = name
self._age = age
def name(self):
return self._name
def age(self):
return self._age
However, until you have a good reason to hide the attribute behind a getter, just expose the attribute as part of the public interface.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
d = Dog("tom", 12)
print(dog.name)
If you later decide to hide the attribute behind a getter and/or setter, you can use a property to do so without changing the public interface.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
#property
def name(self):
return self._name
#name.setter
def name(self, v):
self._name = v
d = Dog("tom", 12)
print(dog.name) # Works the same as before
name is a variable and name is also a function.
Therefore this error.
Just do print(self.name)
you have to create class and change the name of funtion like this
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def Name(self):
return self.name
def age(self):
return self.age
dog = Dog("tom", 12)
print(dog.name())
print(dog.age())
I have this code
class Person(object):
def __init__(self, name):
self.name = name
#classmethod
def from_classmethod(cls, name):
return cls(name)
p = Person.from_classmethod("Moctar")
p.name
But it shows the following error:
AttributeError: 'Person' object has no attribute 'name'
What could be going wrong here, or am i using wrongly the python #classmethod feature ?
As #furas says, what I think you want is:
class Person(object):
def __init__(self, name):
self.name = name
#classmethod
def from_classmethod(cls, name):
return cls(name)
p = Person.from_classmethod("Moctar")
print(p.name)
Result:
Moctar
It is the assignment to self.name that creates that attribute on the instance of Person that you are creating.
In a base class I'm creating a property with property() function:
from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
#abstractmethod
def __init__(self, name, telephone):
self.name = name
self.telephone = telephone
def get_name(self) -> str:
return self.__name
def set_name(self, value: str):
self.__name = value
name = property(fget=get_name, fset=set_name)
And in the derived class I need to override the set_name method, that is, Person.name.fset should point to a modified version of the method. I tried this but it did not work:
class Employee(Person):
def __init__(self, name, telephone, email):
super().__init__(name, telephone)
self.email = email
def set_name(self, value):
super().set_name('override')
Person.name.fset = set_name
Is there any way to override property in python?
I have a class X which derives from a class with its own metaclass Meta. I want to also derive X from the declarative base in SQL Alchemy. But I can't do the simple
def class MyBase(metaclass = Meta):
#...
def class X(declarative_base(), MyBase):
#...
since I would get metaclass conflict error: 'the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases'. I understand that I need to create a new metaclass that would derive from both Meta and from whatever metaclass the declarative base uses (DeclarativeMeta I think?). So is it enough to write:
def class NewMeta(Meta, DeclarativeMeta): pass
def class MyBase(metaclass = NewMeta):
#...
def class X(declarative_base(), MyBase):
#...
I tried this, and it seems to work; but I'm afraid I may have introduced some problem with this code.
I read the manual, but it's a bit too cryptic for me. What's
EDIT:
The code used for my classes is as follows:
class IterRegistry(type):
def __new__(cls, name, bases, attr):
attr['_registry'] = {}
attr['_frozen'] = False
print(name, bases)
print(type(cls))
return type.__new__(cls, name, bases, attr)
def __iter__(cls):
return iter(cls._registry.values())
class SQLEnumMeta(IterRegistry, DeclarativeMeta): pass
class EnumType(metaclass = IterRegistry):
def __init__(self, token):
if hasattr(self, 'token'):
return
self.token = token
self.id = len(type(self)._registry)
type(self)._registry[token] = self
def __new__(cls, token):
if token in cls._registry:
return cls._registry[token]
else:
if cls._frozen:
raise TypeError('No more instances allowed')
else:
return object.__new__(cls)
#classmethod
def freeze(cls):
cls._frozen = True
def __repr__(self):
return self.token
#classmethod
def instance(cls, token):
return cls._registry[token]
class C1(Base, EnumType, metaclass = SQLEnumMeta):
__tablename__ = 'c1'
#...
Edit: Now having looked at IterRegistry and DeclarativeMeta, I think you're code is okay.
IterRegistry defines __new__ and __iter__, while DeclarativeMeta defines __init__ and __setattr__. Since there is no overlap, there's no direct need to call super. Nevertheless, it would good to do so, to future-proof your code.
Do you have control over the definition of Meta? Can you show us its definition? I don't think we can say it works or does not work unless we see the definition of Meta.
For example, there is a potential problem if your Meta does not call
super(Meta,cls).__init__(classname, bases, dict_)
If you run this code
class DeclarativeMeta(type):
def __init__(cls, classname, bases, dict_):
print('DeclarativeMeta')
# if '_decl_class_registry' in cls.__dict__:
# return type.__init__(cls, classname, bases, dict_)
# _as_declarative(cls, classname, dict_)
return type.__init__(cls, classname, bases, dict_)
class Meta(type):
def __init__(cls, classname, bases, dict_):
print('Meta')
return type.__init__(cls, classname, bases, dict_)
class NewMeta(Meta,DeclarativeMeta): pass
class MyBase(object):
__metaclass__ = NewMeta
pass
Then only the string 'Meta' gets printed.
In other words, only Meta.__init__ gets run. DeclarativeMeta.__init__ gets skipped.
On the other hand, if you define
class Meta(type):
def __init__(cls, classname, bases, dict_):
print('Meta')
return super(Meta,cls).__init__(classname, bases, dict_)
Then both Meta.__init__ and DeclarativeMeta.__init__ gets run.
class a(type):
def __str__(self):
return 'aaa'
def __new__(cls, name, bases, attrs):
attrs['cool']='cool!!!!'
new_class = super(a,cls).__new__(cls, name, bases, attrs)
#if 'media' not in attrs:
#new_class.media ='media'
return new_class
class b(object):
__metaclass__=a
def __str__(self):
return 'bbb'
print b
print b()['cool']#how can i print 'cool!!!!'
print b().cool
attrs in your __new__ method becomes the object's dictionary. Properties of Python objects are referenced with the . syntax.
print "cool!!!"
Or did I miss something?