Accessing derived attributes in a named tuple from parent class - python

I have a simple named tuple which functions as a immutable container that characterize a class I designed.
From this Class (that has the namedtuple as class variable), I dervied multiple childclasses that all override this attribute and add new fields to the named tuple
I want to keep the fields that were defined in the Parent class ID and only add the ones that are new. I assume you could simply keep the old ID in the class and do something like ID.2nd = "added form child"
But I'd much prefere if you could simply override the ID variable and acess the previously defined ID's via a call to super() or something
from collections import namedtuple
ID = namedtuple("myTuple", ["first", "second", "third", "fourth"])
ID.__new__.__defaults__ = ("default from parentclass", None , None , None)
class Parent(object):
_ID = ID()
def __init__(self, arg):
self.arg = arg
class Child(Parent):
#if there is a new _ID defined in Child
#, keep the fields it adds but also add the
#ones from the parent:
_ID = ID(second="adding from child")
#it should now contain the fields _ID.first == "default from parent" from parentclass
#and _ID.second == "adding from child"
So far this works, but if I have another child now
class Child2(Child):
_ID = ID(third="adding from child")
#and now something like _ID.second = super()._ID.second
I will lose all information that was added in the intermediate classes.

Related

How to get attributes in class that extends from the same parent class

There are a few classes that I defined
class Animal:
def do_parent_method():
pass
class Monkey(Animal):
pass
class Elephant(Animal):
pass
#dataclass
class Zoo:
monkey: Monkey= Monkey()
elephant: Elephant = Elephant()
start_time: datetime = None
name: str = 'Not important at all'
def data_format(self):
items = [self.monkey, self.elephant] # Now I hard code here
for item in items:
do_something()
The key point is about how to get attributes in the Zoo class
Maybe someday, we will add another animal in our code
#dataclass
class Zoo:
monkey: Monkey= Monkey()
elephant: Elephant = Elephant()
start_time: datetime = None
name: str = 'Not important at all'
def data_format(self):
items = [get the attributes that extends from Animal] # How to do?
for item in items:
do_parent_method()
For now I just want items to be a list, so that I could for-loop it.
Or if you have another good idea is also good for me.
Note:
The all the attributes in Zoom class will only have some str, datetime, int type. All the other instance will be the children class of Animal class.
Fixed:
Accidentally entered 'zoo' into 'zoom'
The dataclasses.fields function can return field information about a class, including both the name and type of each field. So your list comprehension can be written:
items = [getattr(self, field.name) for field in fields(self) if issubclass(field.type, Animal)]
The flaw here is that it doesn't work for string annotations, which includes all cases where the module uses from __future__ import annotations. You could use the tricks here to resolve to the actual type, or you could just unconditionally get all the fields, then filter them with isinstance checks (that verify the runtime type, not the annotated type that can be blithely ignored at runtime):
items = [attr for attr in (getattr(self, field.name) for field in fields(self)) if isinstance(attr, Animal)]

How to access objects of class and instance variable?

I want to know how many objects I have created in a Product class and print all names stored in the class. And is there any way to store all objects in JSON format which has been defined in Product Class?
class Product:
pass
a=Product()
a.name="Pune"
a.apple=2
b=Product()
b.name="Delhi"
b.apple=4
without assigning count in class if I want to know how much I have stored data in that class. what should I do? Is there any better way to access the "apple"
an instance variable of every instance object.
Is there any way to convert class object in JSON format like:
[{"name": "Pune", "apple":2},
{"name": "Delhi", "apple":4}]
You can use __init__ and class variable to do that.
Whenever you create an object of Product class your can add those instance variable to class list
class Product:
names = []
apples = []
def __init__(self, name, apple):
# self.name = name
# self.apple = apple
Product.names.append(name)
Product.apples.append(apple)
a=Product("Pune", 2)
b=Product("Delhi", 4)
print(Product.names)
Output:
['Pune', 'Delhi']

List attributes of a sublass instance in Python

I'm trying to build some "ORM like" behavior in Python. To do this I have a Model class and would like to produce any kind of subclasses. My problem is when I try to list the attributes of my subclasses instances.
Here is my Model class:
class Model:
def __init__(self):
self.className = type(self).__name__
def listAttributes(self):
from modules.users.user import User
instance = User()
meta = vars(instance)
And a User subclass :
class User(models.Model):
name = models.FieldChar()
password = models.FieldChar()
age = models.FieldInteger()
When I invoke the listAttributes method from a User instance, I don't have what I expect, but probably what I should: the meta var contains only className. Here's how I invoke the method:
from modules.users import user
user = user.User()
user.listAttributes()
Same behavior when I override the listAttributes method in User():
def listAttributes(self):
meta = vars(self)
print(meta)
Is there a way to list the attribute of the object from which I call the listAttributes method?
Thanks for your lights!
You could make your listAttributes method a classmethod and try the following:
class Model(object):
#classmethod
def listAttributes(cls):
return [
a for a in dir(cls) if not
callable(getattr(cls, a)) and not
a.startswith("__")
]
If you just want to have the attributes that are of your "Fields" type (FieldChar, FieldInt...) then you could make a base class for the fields called e.g. ModelField and also check your attributes against this base type:
class Model(object):
#classmethod
def listAttributes(cls):
attributes = []
for a in dir(cls):
if ((not callable(getattr(cls, a)) and not
a.startswith("__") and
getattr(cls, a).__class__.__bases__[0] == ModelField)):
attributes.append(a)
You could even return the objects instead of just the names. Or append the objects to a dict in your Model class (to make usage in your code easier). I constructed my ORM like this:
class ModelField(object):
...
class IntField(ModelField):
...
class Model(object):
id = IntField(pk=True)
def __init__(self, *args, **kwargs):
try:
self.modelFields
except AttributeError:
self.__class__.introspect()
for key, modelfield in self.modelFields.items():
setattr(self, key, kwargs.get(key, None))
#classmethod
def introspect(cls):
cls.modelFields = {}
for a in dir(cls):
if ((not callable(getattr(cls, a)) and not
a.startswith("__") and
getattr(cls, a).__class__.__bases__[0] == ModelField)):
cls.modelFields[a] = getattr(cls, a)
Then i used cls.introspect to set up the modelFields dict and use this dict to e.g. autogenerate a db-table or construct querys by using attributes of the ModelField class.

Python - updating class attribute (monkey patching?)?

So there is a class called Field and it has class attribute _slots, this attribute is dictionary.
So it looks like this (that class also use custom __metaclass__):
class Field(object):
_slots = {
'key1': False,
'key2': None,
...
}
Class content is too big to be pasted here, so I'm providing a link to it, src
Now I need that when you instantiate this class, it will have my custom key/value pair inside that attribute.
So I tried this (in my own module):
from openerp import fields
fields.Field._slots['custom_key'] = None
When I print after this assignment, then it shows me that I have set this key/value pair, but actually when that class is loaded, it never gets my custom_key. Though if I monkey patch some method, it works, but for class attribute it does not. Is it even possible to do that (or am I doing something wrong here?)?
Though if I write simple test scenario with my own simple class, like:
(f.py file)
class F(object):
_t = {'a': 1}
(other python file)
import f
f.F._t['b'] = 10
f_new = f.F()
print f_new._t
Then this one works.
Update
I also tried this (modifying code directly) in MetaField class that is used as __metaclass for Field (still that is ignored when Field class uses _slots:
def __new__(meta, name, bases, attrs):
""" Combine the ``_slots`` dict from parent classes, and determine
``__slots__`` for them on the new class.
"""
base_slots = {}
for base in reversed(bases):
base_slots.update(getattr(base, '_slots', ()))
slots = dict(base_slots)
slots.update(attrs.get('_slots', ()))
attrs['__slots__'] = set(slots) - set(base_slots)
attrs['_slots'] = slots
attrs['_slots']['custom_key'] = None # <= Modification here
return type.__new__(meta, name, bases, attrs)

Python class inheritance issue occurs when using base class to assign attributes of subclass

When using base class to create sub class attribute, I got an error that sub class has no member which I created in the base class method. Codes are shown below
class Base(object):
def setAttributes(self, data):
for key in data:
self.key = data[key]
class Sub(Base):
pass
data = {'id':1, 'name': 'Name'}
a = Sub()
Base.setAttributes(a, data)
print(a.id)
And the error messages are : AttributeError: 'Sub' object has no attribute 'id'.
Is there any solution to use base class to assign sub class attributes in python?
This has nothing to do with inheritance. You have assigned the value to a.key not a.id. In fact you have assigned all your values to a.key.
I believe you want setattr(self, key, data[key]) in place of self.key = data[key]. Or just do away with the loop entirely and do self.__dict__.update(data).

Categories

Resources