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)
Related
The class I have been using looks simple, like this:
class Transaction(dict):
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
and then sending in:
transaction = Transaction({"to": "0x000", "from": "0x001": "timestamp": 1234})
and of course can be used like this transaction.to, however it looks like transaction.from does not work because from is a python reserved keyword
So I am curious using that simple class, is there a way to reassign from in the class to be something like
self.sender = dict.from
I have been trying with __init__ but with no luck
I also have written the class just with an __init__ and then assigning all values using self but with out a getter the class is not iterable
What I have been doing looks like this
# given data - {"to": "0x000", "from": "0x001": "timestamp": 1234}
item["sender"] = item["from"]
transaction = Transaction(item)
and then I have reference to it like transaction.sender.
If I understand correctly, your end goal is to have a class that can be instantiated from a dict and expose the keys as attributes. I'm inferring that the dict can only contain certain keys since you're talking about mapping "from" to sender. In that case, I would do this completely differently: don't subclass dict, instead have an alternate constructor that can handle the dict. I'd keep a "normal" constructor mostly for the sake of the repr.
For example:
class Transaction:
def __init__(self, to, from_, timestamp):
self.to = to
self.from_ = from_
self.timestamp = timestamp
#classmethod
def from_dict(cls, d):
return cls(d['to'], d['from'], d['timestamp'])
def __repr__(self):
"""Show construction."""
r = '{}({!r}, {!r}, {!r})'.format(
type(self).__name__,
self.to,
self.from_,
self.timestamp)
return r
transaction = Transaction.from_dict({"to": "0x000", "from": "0x001", "timestamp": 1234})
print(transaction) # -> Transaction('0x000', '0x001', 1234)
print(transaction.from_) # -> 0x001
Here I'm using the trailing underscore convention covered in PEP 8:
single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.
tkinter.Toplevel(master, class_='ClassName')
By the way, if it's useful, the keyword module contains the names of all Python keywords.
Suppose I have a python class like:
class User:
name = None
id = None
dob = None
def __init__(self, id):
self.id = id
Now I am doing something like this:
userObj = User(id=12) # suppose I don't have values for name and dob yet
## some code here and this code gives me name and dob data in dictionary, suppose a function call
user = get_user_data() # this returns the dictionary like {'name': 'John', 'dob': '1992-07-12'}
Now, the way to assign data to user object is userObj.name = user['name'] and userObj.dob = user['dob']. Suppose, User has 100 attributes. I will have to explicitly assign these attributes. Is there an efficient way in Python which I can use to assign the values from a dictionary to the corresponding attributes in the object? Like, name key in the dictionary is assigned to the name attribute in the object.
1. Modify the Class definition
class User():
def __init__(self, id):
self.data = {"id":id}
userObj = User(id=12)
2. Update the dict()
user = {"name":"Frank", "dob":"Whatever"} # Get the remaining data from elsewhere
userObj.data.update(user) # Update the dict in your userObj
print(userObj.data)
Here you go !
Instead of mapping a dict to the variable keys. You can use setattr to set variables in an object.
class User:
name = None
id = None
dob = None
def __init__(self, id):
self.id = id
def map_dict(self, user_info):
for k, v in user_info.items():
setattr(self, k, v)
Then for boiler code to use it.
userObj = User(id=12)
user_dict = {
'name': 'Bob',
'dob': '11-20-1993',
'something': 'blah'
}
userObj.map_dict(user_dict)
First, there is no need to predeclare properties in python.
class Foo:
bar: int # This actually creates a class member, not an instance member
...
If you want to add values to a class instance just use setattr()
d = {
'prop1': 'value1',
'prop2': 'value2',
'prop2': 'value2'
}
x = Foo()
for prop in d.keys():
setattr(x, prop, d[prop])
class User(dict):
def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
self.__dict__ = self
and then just get your dictionary and do:
userObj = User(dictionary)
EDIT:
user the function setattr() then
[setattr(userObj, key, item) for key,item in dict.items()]
In Case you REALLY need to
This solution is for the case, other solutions dont work for you and you cannot change your class.
Issue
In case you cannot modify your class in any way and you have a dictionary, that contains the information you want to put in your object, you can first get the custom members of your class by using the inspect module:
import inspect
import numpy as np
members = inspect.getmembers(User)
Extract your custom attributes from all members by:
allowed = ["__" not in a[0] for a in members]
and use numpy list comprehention for the extraction itself:
members = np.array(members)["__" not in a[0] for a in members]
Modify the user
So lets say you have the following user and dict and you want to change the users attributes to the values in the dictionary (behaviour for creating a new user is the same)
user = User(1)
dic = {"name":"test", "id": 2, "dob" : "any"}
then you simply use setattr():
for m in members:
setattr(user, m[0], dic[m[0]])
For sure there are better solutins, but this might come in handy in case other things dont work for you
Update
This solution uses the attribute definitions based on your class you use. So in case the dictionary has missing values, this solution might be helpful. Else Rashids solution will work well for you too
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.
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.
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).