I'm currently reading this amazing book called "Dive into Python". Up to now everything has made sense to me, but the following method has left me with some questions. Its in the chapter about initializing classes:
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
It's only the last line I don't get. The way I see it at the moment, the calling object has a list, whose item "name" is assigned the value of the argument passed. But this doesn't make sense to me, since I thought that you can only access list indices by integers.
The book says the following about this line: "You're assigning the argument filename as the value of this object's name key." Is the name key another variable that every object defines (like doc)? And if yes, why can it be accessed like that?
[...] isn't just for lists. Any type can support it, and the index doesn't necessarily have to be an integer. self is the current object, which according to your code derives from UserDict, which supports the item manipulation methods.
You're extending a dictionary, by doing class FileInfo(UserDict), that's why you can reference to the key doing self['name'] = filename
The class inherits from UserDict which I presume is a dict-like class. For all subclasses of dicts (which keeps the dict interface intact), you can treat self as a dict, which is why you can do self[key] = value
No, the self object is a subclass of UserDict, which is a form of hash table (known as a dictionary or dict in Python). The last line is simply creating a key "name" to the filename.
Since your class derives from UserDict, it inherits a __getitem__() method that takes an arbitrary key, not just an integer:
self["name"] = filename # Associate the filename with the "name" key.
Related
I'm trying to keep a bit of code as efficient as possible and I think the aspect of it is just messy. If anyone want's to give a suggestion for a simplification of it I would really apreciate it.
result = DotDict()
for name in dir(obj):
value = getattr(obj, name)
if not name.startswith('__') and not ismethod(value):
result[name] = value
return result
In this case, the DotDict is a user class, derived from the dict class, being the only important thing the notion that it follows the same process that a dict does. The obj variable will any time, but will be mainly used as a class object from other classes.
I've seen a loop inside the dict creation but since I need both the name and value for the creation I couldn't make it work.
I've created a subclass of dict as per this question. What I'd like to do is be able to create a new dictionary of my subclass by using bracket notation. Namely, when I have {"key":"value"}, I'd like it to call mydict(("key","value")). Is this possible?
No. And for good reasons: it would violate the expectations of people reading the code. It would also likely break any third-party libraries that you import which would (reasonably) expect dict literals to be standard python dictionaries.
A better method to fix this without lots of extra typing is to simply add a static .from method on your custom dictionary class that attempts to consume the literal and returns an instance of your custom dictionary.
MyDict.from({
"key": "value"
})
Where an implementation of from might look something like
#classmethod
def from(cls, dictionary):
new_inst = cls()
for key, value of dictionary.items():
new_inst[key] = value
return newInst
Edit based on comment:
user2357112 correctly points out that you could just use the constructor as long as the dict constructor's signature is the same as your custom class:
some_instance = MyDict({"key": "value"})
If you've messed with it though you'll have to go the custom route a la from.
I successfully print a list of keys to the screen and then ask the user to type in a key name to get a specific object. This is the way i thought it worked:
print dict['bob']
and this would output the object stored with the 'bob' key however it does not. I'm assuming this problem arises because im storing objects and not strings or ints. If the name entered is a key it just prints the name again not the object and if it isn't a key it throws an error. The end result of this is to get a specified object so the user can view that objects attributes.
When the above statement is ran it just prints the key to the screen:
bob
printing my dictionary looks like this
{'Sal': <Employ_Class2.Employee object at 0x01EE38F0>, 'bob': <Emplo
y_Class2.Employee object at 0x01EE3930>, 'jack': <Employ_Class2.Employee o
bject at 0x01EE3990>, 'alexa': <Employ_Class2.Employee object at 0x01EE3870>,
'dave': <Employ_Class2.Employee object at 0x01EE3910>, 'sew
': <Employ_Class2.Employee object at 0x01EE3950>, 'tam': <Employ_Class2.Em
ployee object at 0x01EE39D0>}
It looks like you're simply being misled by the fact that print obj prints the result of calling the object's __str__ or __unicode__ methods. So, the object in the dictionary under the key "bob", when converted to string, is simply "bob".
As others have said, if you simply want to print the attributes of the object under that key, use dir:
obj = dict['bob']
print dir(obj)
If you want to view the names of the attributes of the object, use dir().
If the dictionary contains the instances of the class you've created and you want to extract relevant information from those objects, you need to create in your class a function which does that and invoke it later.
Suppose you have an object of type Product, given that Product is the class you've created. You store multiple products in your dictionary. What do you want to get back? The name of the product? The name, the price and the description? Or maybe the class name? Python won't be able to figure out your intention, so it's up to you to either create a specific method which does what you want it to do, or use __str__().
I think what you wanted is dir([object]) .
Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.
So according to the documentation you can have the attributes fro that object with dir().
Hope this is what you have wanted..
Not sure if you are referring to object attributes you are defining in your class definition or the names you would find with dir(). In either case, if your object class has a __str__ or __repr__ methods defined, those will be called when using print. If you want a user to be able to view the names in the object space, just use dir(). If you want them to access attributes and properties you set in the class definition, access them directly (or through whatever methods you created to do so:
class Test:
attr = 'item' # this is a class attribute
def __init__(self):
self.name = 'Me' # this will be an instance variable/property
def __str__(self):
return self.name
d = {} # example does not need a dict but using one to relate to posted question
d['item'] = Test()
print(d['item'])
results in self.name being printed to screen:
dir(d['item']) will instead print out all of the names in the object namespace, including methods and attributes.
To print your created attributes (attr and self.name above), then you can simply do:
print(d['item'].attr)
print(d['item'].name)
So, in that case, your users should just access the dict objects directly: dict['bob'].attribute
Hope that makes some sense.
I have a class like this:
class Rank (models.Model):
respect=models.IntegerField(max_length=3)
#some other attributes
Rank has many attributes (the first of which is respect).
The names of the attributes of Rank are stored in the list, attributes.
I would like to iterate through them, setting them to somevalue, an arbitrary variable.
I'm attempting:
rank=Rank()
for att in attributes:
rank.att=somevalue #I want rank.(value of att) instead of this
I think what you're trying to do is set a value to an attribute by name. This is what the setattr built in method is for.
rank=Rank()
for att in attributes:
setattr(rank, att, somevalue)
You can use the *args or **kwargs wildcard argument. Try the following to see how it works:
def foo(*args, **kwargs):
print args
print kwargs
foo(1,2,3)
foo(a=1,b=2,c=3)
foo(1,2,z=3)
You can inject attributes to objects using setattr
for att in attributes:
setattr(classobj, att.name, att.value)
Note however that this may work or not because the class could have been based on a metaclass that inspects the attributes when creating the class object.
Adding attributes or methods later may work or not, depending on what kind of magic has been used in the metaclass.
If the class wants to inspect the attributes at creation time the only solution (without messing with the class specific magic) is using eval of a string.
In general, you can't pass a name of a variable to a function - python is call by value only*
What you can do, and I think this is what you want, is use getattr and setattr:
for att in attributes:
print getattr(rank, att)
setattr(rank, att, somevalue)
* I'm aware of the sect who believe that Python has some other calling convention. It's not a thing.
I want to add a dictionary that maps an object to a list of objects as an instance variable to a class. What is the idiomatic way to do it in Python? Here's how I've done it:
class MyClass:
def __init__(self):
self.myDict = { None : [None] }
You have already hit upon the idiomatic way to do it!
Personally, I'd do it this way, if you want the option of starting out with a certain number of objects, and you want it clearer what you're desiring.
class MyClass:
def __init__(self, objlistdict=None):
"Takes an optional initial dictionary with objects as keys and lists of objects as values" # docstrings are very useful for documentation
self.myDict = {} if objlist is None else objlist # sets `self.myDict` to be the initial dict, otherwise makes it an empty one
Note that the objects you use as keys do need to be hashable, and I believe it's recommended to use immutable objects even if the mutable object you're using still has a hash method defined, due to the fact that objects that compare equal are generally supposed to have the same hash, so changing something about the object will change the hash value, and that might cause problems with being unable to access the value for an original key.