I get a dict to init a class person. there is one field in person: 'name'. 'name' field is optional, meaning that if the dict don't have the 'name' item, then there's no 'name' value of person. I use getter methods to get instance attribute, but it will throw a error if there's no 'name' value. I don't know is there any good programming style to improve my code? Because python create instance field at run time, I don't know how to use getter like java.
class Person:
def __init__(self,person_dict):
try:
self.name = person_dict['name']
except Exception:
pass
def getName(self):
return self.name
pdict = {}
p = Person(pdict)
print p.getName()
AttributeError: Person instance has no attribute 'name'
class Person:
def __init__(self,person_dict):
self.name = person_dict.get('name')
In this case self.name = person_dict.get('name') won't raise Exception and Person objects will have name attribute (None by default)
UPD. Because of getName method is useless, I cut it down from example. Access name attr directly.
class Person:
def __init__(self,person_dict):
self.name = person_dict.get('name', 'default_name')
pdict = {}
p = Person(pdict)
print p.name # there is no need for getter
If you don't want the exception, then you should make sure the instance has a value for name. Since lookup falls back to the class if an attribute can't be found on the instance, an easy way to do this is to simply add name = None (or whatever default value you want the instance to use) to the class definition. Assignment to the attribute on the instance will "hide" the default value.
class Person:
name = None
def __init__(self,person_dict):
try:
self.name = person_dict['name']
except Exception:
pass
You could instead write your __init__ like this:
def __init__(self,person_dict):
self.name = person_dict.get('name')
The get() method of dictionaries returns None if the key isn't found, or you can provide a second argument with a different default value.
Related
The script below tries to remove the Inner1 nested class from the class Outer. I get the clear error "TypeError: can't delete __class__ attribute". Dead end?
def Loader(cls):
for key in dir(cls):
value = getattr(cls, key)
if isinstance(value, type):
delattr(cls, key)
return cls
#Loader
class Outer:
class Inner1:
X = 1
print(Outer.Inner1.X)
The problem is that the following line:
if isinstance(value, type):
Is matching everything that inherits from type. If you wish to only delete that inner classes that are explicitly defined in the decorated class, you could use something like this:
from inspect import isclass
def Loader(cls):
# vars returns the __dict__ attributes, so only names that are explicitely defined in cls.
for key, value in vars(cls).copy().items():
# If it is a class, delete it
if isclass(value):
delattr(cls, key)
return cls
#Loader
class Outer:
class Inner1:
X = 1
print(Outer.Inner1.X) # AttributeError: type object 'Outer' has no attribute 'Inner1'
An alternative, would be to make your decorator a class and tell it specifically the names you want to delete. E.g.
class Loader:
def __init__(self, *args):
# Store the given names to delete
self.names = args
def __call__(self, cls):
for name in self.names:
try:
delattr(cls, name)
except AttributeError:
pass
return cls
#Loader('Inner1', 'Inner2', 'etc')
class Outer:
class Inner1:
X = 1
print(Outer.Inner1.X) # AttributeError: type object 'Outer' has no attribute 'Inner1'
That said, I'm not sure why you would decorate a class and define inner classes that you will dynamically delete... why not... just not define them at all? :D
I'm struggling to solve with this problem.
I'd like to have the name variable to be like a pointer to the value of self.model.name. If the value of _class.model.name is changed the _class.name should change accordingly.
Can't find a way to basically map dynamically the Class attributes with any Model attributes without inheriting.
class Model(object):
name = 'foo'
parent = 'foo_p'
class Class(object):
model_class = Model
def __init__(self):
self.model = self.model_class()
setattr(self, 'name', self.model.name)
_class = Class()
print _class.model.name # foo
_class.model.name = 'foo_1'
print _class.name # this should be foo_1
Thanks!
Use a property to create a single dynamically computed attributes:
class Class(object):
_model_class = Model
#property
def name(self):
return Class._model_class.name
This causes all instances of Class to run the name method whenever the attribute name is looked up. This allows the value to be dynamically computed on each lookup.
_class = Class()
print(_class.name) # 'foo'
Model.name = 'bar'
print(_class.name) # 'bar'
If you want to dynamically fetch many or all attributes from somewhere else, consider using __getattr__:
class Class(object):
_model_class = Model
def __getattr__(self, name):
# name is a *string* containing the name of the attribute to fetch
return getattr(Class._model_class, name)
The __getattr__ is only triggered for attributes that are not on the class/instance. This makes it rather straightforward to use with manually defined attributes. Note that you can use arbitrary code to restrict what is fetched - e.g. raise AttributeError if name is not in some whitelist.
Along with MisterMiyagi's answer - but in case you want to still want to keep the concerns separated (even though it really doesn't seem like you do)
class Class(object):
model_class = Model
def __init__(self):
self.model = self.model_class()
setattr(self, 'name', self.model.name)
# This is what we'll be changing
#property
def model_name(self):
return self.model.name
# This method will get called whenever we change the model_name
#model_name.setter
def model_name(self, new_name):
self.model.name = new_name
self.name = new_name
_class = Class()
_class.model_name # foo
_class.model_name = "bar" # both _class.model.name == bar and _class.name == bar now
I have a class such as:
class MP3:
name = ""
capacity = 0
def newMP3(name, capacity):
MP3.name = name
MP3.capacity = capacity
My main script:
from mp3class import *
currentMP3 = MP3.newMP3("myName", 10)
print (currentMP3.name)
print (currentMP3.capacity)
However, the print statements return an error:
AttributeError: 'NoneType' object has no attribute 'name'
Why is currentMP3 == None when I've just assigned it?
I've tried return (name, capacity) at the end of class MP3 and that gives me a different error:
AttributeError: 'tuple' object has no attribute 'name'
Even though the tuple does have name in it?
You're returning None implicitly after calling:
def newMP3(name, capacity):
MP3.name = name
MP3.capacity = capacity
and assigning it to the name currentMP3.
You're either looking for __init__ to initialize a new instance with some attributes:
def __init__(self, name, capacity):
self.name = name
self.capacity = capacity
or, as an alternative, you can change the class attributes directly and then create a new instance with #classmethod:
#classmethod
def newMP3(cls, name, capacity):
cls.name = name
cls.capacity = capacity
return cls()
Your newMP3 method in your class doesn't return anything. In Python, that's the same as returning None. So when you do currentMP3 = MP3.newMP3(...), currentMP3 becomes None and doesn't have access to the class attributes you set on the MP3 class.
Note that your use of class attributes and no instances is a very odd one. I'd expect lots of other bugs if you keep going that route. A much more natural implementation would create an instance of the MP3 class and set attributes on the instance.
I'm currently writing my first bigger project in Python, and I'm now wondering how to define a class method so that you can execute it in the class body of a subclass of the class.
First to give some more context, a slacked down (I removed everything non essential for this question) example of how I'd do the thing I'm trying to do in Ruby:
If I define a class Item like this:
class Item
def initialize(data={})
#data = data
end
def self.define_field(name)
define_method("#{name}"){ instance_variable_get("#data")[name.to_s] }
define_method("#{name}=") do |value|
instance_variable_get("#data")[name.to_s] = value
end
end
end
I can use it like this:
class MyItem < Item
define_field("name")
end
item = MyItem.new
item.name = "World"
puts "Hello #{item.name}!"
Now so far I tried achieving something similar in Python, but I'm not happy with the result I've got so far:
class ItemField(object):
def __init__(self, name):
self.name = name
def __get__(self, item, owner=None):
return item.values[self.name]
def __set__(self, item, value):
item.values[self.name] = value
def __delete__(self, item):
del item.values[self.name]
class Item(object):
def __init__(self, data=None):
if data == None: data = {}
self.values = data
for field in type(self).fields:
self.values[field.name] = None
setattr(self, field.name, field)
#classmethod
def define_field(cls, name):
if not hasattr(cls, "fields"): cls.fields = []
cls.fields.append(ItemField(name, default))
Now I don't know how I can call define_field from withing a subclass's body. This is what I wished that it was possible:
class MyItem(Item):
define_field("name")
item = MyItem({"name": "World"})
puts "Hello {}!".format(item.name)
item.name = "reader"
puts "Hello {}!".format(item.name)
There's this similar question but none of the answers are really satisfying, somebody recommends caling the function with __func__() but I guess I can't do that, because I can't get a reference to the class from within its anonymous body (please correct me if I'm wrong about this.)
Somebody else pointed out that it's better to use a module level function for doing this which I also think would be the easiest way, however the main intention of me doing this is to make the implementation of subclasses clean and having to load that module function wouldn't be to nice either. (Also I'd have to do the function call outside the class body and I don't know but I think this is messy.)
So basically I think my approach is wrong, because Python wasn't designed to allow this kind of thing to be done. What would be the best way to achieve something as in the Ruby example with Python?
(If there's no better way I've already thought about just having a method in the subclass which returns an array of the parameters for the define_field method.)
Perhaps calling a class method isn't the right route here. I'm not quite up to speed on exactly how and when Python creates classes, but my guess is that the class object doesn't yet exist when you'd call the class method to create an attribute.
It looks like you want to create something like a record. First, note that Python allows you to add attributes to your user-created classes after creation:
class Foo(object):
pass
>>> foo = Foo()
>>> foo.x = 42
>>> foo.x
42
Maybe you want to constrain which attributes the user can set. Here's one way.
class Item(object):
def __init__(self):
if type(self) is Item:
raise NotImplementedError("Item must be subclassed.")
def __setattr__(self, name, value):
if name not in self.fields:
raise AttributeError("Invalid attribute name.")
else:
self.__dict__[name] = value
class MyItem(Item):
fields = ("foo", "bar", "baz")
So that:
>>> m = MyItem()
>>> m.foo = 42 # works
>>> m.bar = "hello" # works
>>> m.test = 12 # raises AttributeError
Lastly, the above allows you the user subclass Item without defining fields, like such:
class MyItem(Item):
pass
This will result in a cryptic attribute error saying that the attribute fields could not be found. You can require that the fields attribute be defined at the time of class creation by using metaclasses. Furthermore, you can abstract away the need for the user to specify the metaclass by inheriting from a superclass that you've written to use the metaclass:
class ItemMetaclass(type):
def __new__(cls, clsname, bases, dct):
if "fields" not in dct:
raise TypeError("Subclass must define 'fields'.")
return type.__new__(cls, clsname, bases, dct)
class Item(object):
__metaclass__ = ItemMetaclass
fields = None
def __init__(self):
if type(self) == Item:
raise NotImplementedError("Must subclass Type.")
def __setattr__(self, name, value):
if name in self.fields:
self.__dict__[name] = value
else:
raise AttributeError("The item has no such attribute.")
class MyItem(Item):
fields = ("one", "two", "three")
You're almost there! If I understand you correctly:
class Item(object):
def __init__(self, data=None):
fields = data or {}
for field, value in data.items():
if hasattr(self, field):
setattr(self, field, value)
#classmethod
def define_field(cls, name):
setattr(cls, name, None)
EDIT: As far as I know, it's not possible to access the class being defined while defining it. You can however call the method on the __init__ method:
class Something(Item):
def __init__(self):
type(self).define_field("name")
But then you're just reinventing the wheel.
When defining a class, you cannot reference the class itself inside its own definition block. So you have to call define_field(...) on MyItem after its definition. E.g.,
class MyItem(Item):
pass
MyItem.define_field("name")
item = MyItem({"name": "World"})
print("Hello {}!".format(item.name))
item.name = "reader"
print("Hello {}!".format(item.name))
I am trying to find a way to set dict values encapsulated into a class, for example using __getattr__ i can return the internal dict value, however the __setattr__ is called even when attributes exists, making my implementation ugly. The example below is simplified my actual class inherits from a Subject class (the subject part of the observer pattern)
i am trying to achieve something like this:
obj = Example()
obj.username = 'spidername' # all OK username is a key in the internal dict
# but company is not a key in the internal dict so
obj.company = 'ABC' # will raise AttributeError
and i am asking if there is a better way than the way i am doing below:
class Example(object):
def __init__(self, table=None):
self._fields = {}
self._table = table
def _set_fields(self):
"""
this method will be implemented by
subclasses and used to set fields names and values
i.e.
self._field['username'] = Field(default='unknown', is_primary=False)
"""
raise NotImplementedError
def __getattr__(self, name):
"""
great this method is only called when "name"
is not an attribute of this class
"""
if name in self._fields:
return self._fields[name].value
return None
def __setattr__(self, name, value):
"""
not so great, this method is called even for
attributes that exists in this class
is there a better way to do the following?
this can be in __init__, but its still ugly
"""
attribs = ['_fields', '_table']
if name in attribs:
super(Example, self).__setattr__(name, value)
else:
if name in self._fields:
self._fields[name].value = value
else:
raise AttributeError
EDIT: adjusted comment in code, added missin quotes
The problem is that the attributes don't exist when they are first assigned. In __init__, when you first assign a dict to _fields, _fields is not an attribute. It only becomes an existing attribute after its been assigned. You could use __slots__ if you know in advance what the attributes are, but my guess is that you don't. So my suggestion would be to insert these into the instance dict manually:
class Example(object):
def __init__(self, table=None):
self.__dict__['_fields'] = {}
self.__dict__['_table'] = table
...
def __setattr__(self, name, value):
if name in self._fields:
self._fields[name].value = value
else:
raise AttributeError
However, with this implementation, the only way you can add or change instance attributes later would be through __dict__. But I assume this is not likely.
FWIW, your overall goal can be achieved directly just by using __slots__:
>>> class Example(object):
__slots__ = ['username']
>>> obj = Example()
>>> obj.username = 'spiderman'
>>> obj.company = 'ABC'
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
obj.company = 'ABC'
AttributeError: 'Example' object has no attribute 'company'