Using an instance of a class as an attribute of a class, descriptors, and properties

I recently said while trying to use a newer style of classes in Python (those that are derived from an object). As an excersise, to familiarize myself with them, I am trying to define a class that has several class instances as attributes, each of these class instances describing different data types, for example. 1d, 2d arrays, scalars, etc. In fact, I want to write

some_class.data_type.some_variable 

where data_type is an instance of a class that describes a set of variables. Below was my first attempt to implement this using only an instance of profiles_1d and fairly common names:

 class profiles_1d(object): def __init__(self, x, y1=None, y2=None, y3=None): self.x = x self.y1 = y1 self.y2 = y2 self.y3 = y3 class collection(object): def __init__(self): self._profiles_1d = None def get_profiles(self): return self._profiles_1d def set_profiles(self, x, *args, **kwargs): self._profiles_1d = profiles_1d(x, *args, **kwargs) def del_profiles(self): self._profiles_1d = None profiles1d = property(fget=get_profiles, fset=set_profiles, fdel=del_profiles, doc="One dimensional profiles") 

Is the above code an approximately appropriate way to solve this problem. The examples I saw when using property simply set the value of some variable. Here, I need my set method to initialize an instance of some class. If not, any other suggestions on better ways to implement this would be greatly appreciated.

Also, how do I define my set ok method? Typically, the set method, as I understand it, determines what to do when the user types in this example.

 collection.profiles1d = ... 

The only way I can correctly set the attributes of the profiles_1d instance with the above code is to enter collection.set_profiles([...], y1=[...], ...) , but I think I shouldn't access this method directly. Ideally, I would like to type collection.profiles = ([...], y1=[...], ...) : is this correct / possible?

Finally, I saw decorators mention a new style of activity, but I know very little about it. Is it possible to use decorators here? Is this something I should know more about for this problem?

+8
python properties class
source share
2 answers

First of all, itโ€™s good that you are learning new style classes. They have many advantages.

A modern way to create properties in Python:

 class Collection(object): def __init__(self): self._profiles_1d = None @property def profiles(self): """One dimensional profiles""" return self._profiles_1d @profiles.setter def profiles(self, argtuple): args, kwargs = argtuple self._profiles_1d = profiles_1d(*args, **kwargs) @profiles.deleter def profiles(self): self._profiles_1d = None 

then install profiles by doing

 collection = Collection() collection.profiles = (arg1, arg2, arg3), {'kwarg1':val1, 'kwarg2':val2} 

Pay attention to all three methods with the same name.

This is usually not done; either pass the attributes to the collection constructor, or create them yourself profiles_1d , and then make collections.profiles = myprofiles1d or pass it to the constructor.

If you want the attribute to control access to itself, and not to the class that controls access to the attribute, make the attribute a class with a descriptor. Do this if, unlike the properties example above, you really need the data stored inside the attribute (instead of the other, the faux-private instance variable). In addition, it is good if you intend to use the same property again and again - make it a descriptor, and you do not need to write code several times or use the base class.

I really like the @ S.Lott page - Building Skills in Python Attributes, Properties and Descriptors .

+10
source share

When creating a property (or other descriptors) that must invoke other instance methods, the naming convention is to add _ to these methods; so your names above would be _get_profiles , _set_profiles and _del_profiles .

In Python 2.6+, each property is also a decorator, so you do not need to create (otherwise useless) _name methods:

 @property def test(self): return self._test @test.setter def test(self, newvalue): # validate newvalue if necessary self._test = newvalue @test.deleter def test(self): del self._test 

It looks like your code is trying to set profiles in the class instead of instances - if so, the properties in the class will not work, since collections.profiles will be overridden by the profiles_1d object clobbering property ... if this is really what you want, you will have to create a metaclass and place the property there instead.

I hope you are talking about examples, so the class will look like this:

 class Collection(object): # notice the capital C in Collection def __init__(self): self._profiles_1d = None @property def profiles1d(self): "One dimensional profiles" return self._profiles_1d @profiles1d.setter def profiles1d(self, value): self._profiles_1d = profiles_1d(*value) @profiles1d.deleter def profiles1d(self): del self._profiles_1d 

and then you will do something like:

 collection = Collection() collection.profiles1d = x, y1, y2, y3 

A few notes: the setter method is called with only two elements: self and a new value (so you had to manually call set_profiles1d ); when assignment is performed, the keyword is not an option (which only works in function calls, and assignment is not). If this makes sense to you, you can get a fantasy and do something like:

 collection.profiles1d = (x, dict(y1=y1, y2=y2, y3=y3)) 

and then change the setter value to:

  @profiles1d.setter def profiles1d(self, value): x, y = value self._profiles_1d = profiles_1d(x, **y) 

which is still readable enough (although I prefer the version of x, y1, y2, y3 ).

+1
source share

All Articles