I have class with hundreds of methods
I want create a hierarchy of them that will let easy find method.
For example
class MyClass:
def SpectrumFrequencyStart()...
def SpectrumFrequencyStop()...
def SpectrumFrequencyCenter()...
def SignalAmplitudedBm()...
That I want to call using:
MyClassObject.Spectrum.Frequency.Start()
MyClassObject.Spectrum.Frequency.Stop()
MyClassObject.Signal.Amplitude.dBm()
Consider using a dictionary to map your methods to keys (either hierarchical dictionaries, or simply '.' separated keys).
Another option which may be more elegant is namedtuples. Something like:
from collections import namedtuple
MyClassObject = namedtuple('MyClassObject', ['Spectrum', 'Signal'])
MyClassObject.Spectrum = namedtuple('Spectrum', ['Frequency'])
MyClassObject.Spectrum.Frequency = namedtuple('Frequency', ['Start', 'Stop'])
MyClassObject.Spectrum.Frequency.Start = MyClass.SpectrumFrequencyStart
You can automate this by using inspection and parse the method names by, say camel case, to build the namedtuples automatically.
Pay attention to binding of the methods
This is just a very bad design.
It's clear that Spectrum, Signal, Frequency (and so on) should be all separate classes with much less than "hundreds of methods".
I'm not sure if MyClassObject actually represents something or is effectively just a namespace.
Objects can encapsulate objects of other classes. For example:
class Frequency(object):
def start(self):
pass
def stop(self):
pass
class Spectrum(object):
def __init__(self):
self.frequency = Frequency()
class Amplitude(object):
def dbm(self):
pass
class Signal(object):
def __init__(self):
self.amplitude = Amplitude()
class MyClass(object):
def __init__(self):
self.spectrum = Spectrum()
self.signal = Signal()
my_class_instance = MyClass()
my_class_instance.spectrum.frequency.start()
my_class_instance.spectrum.frequency.stop()
my_class_instance.spectrum.signal.amplitude.dbm()
There's a convention of code formatting in Python PEP 8 therefore I applied it in my example.
Related
Suppose I have a set of (possibly abstract) base classes which cooperate in a certain way, and I want to subclass them in such a way that the subclasses are aware of its respective co-operating subclasses (e.g. it has the other classes as class attributes).
Literally adding attributes seems really messy for more than a handful of classes.
One way I can think of doing this is to class properties for the abstract classes which would reference a dictionary class attribute (same dictionary for all classes), via mixin to avoid repeating code in the superclass module. This way, I only need to add one attribute for each subclass (and add a dictionary referencing all the classes in the module), see the code below.
Is there an established design pattern to achieve this sort of thing?
Example:
abstract_module:
from abc import ABC
_module_classes_dict = {}
class _ClassesDictMixin:
_classes_dict = dict()
#classmethod
#property
def _a_class(cls):
return cls._classes_dict['a']
#classmethod
#property
def _b_class(cls):
return cls._classes_dict['b']
#classmethod
#property
def _c_class(cls):
return cls._classes_dict['c']
class AbstractA(ABC):
pass
class AbstractB(_ClassesDictMixin, ABC):
_classes_dict = _module_classes_dict
# # Basic solution without using the dict
# _a_class = AbstractA
class AbstractC(_ClassesDictMixin, ABC):
_classes_dict = _module_classes_dict
# # Basic solution without using the dict
# _a_class = AbstractA
# _b_class = AbstractB
class AbstractD(_ClassesDictMixin, ABC):
_classes_dict = _module_classes_dict
# # Alternative solution without using the dict
# _a_class = AbstractA
# _b_class = AbstractB
# _c_class = AbstractC
_module_classes_dict.update(a=AbstractA, b=AbstractB, c=AbstractC, d=AbstractD)
concrete_module:
from abstract_module import AbstractA, AbstractB, AbstractC, AbstractD
_module_classes_dict = {}
class ConcreteA(AbstractA):
pass
class ConcreteB(AbstractB):
_classes_dict = _module_classes_dict
# # Basic solution without using the dict
# _a_class = ConcreteA
class ConcreteC(AbstractC):
_classes_dict = _module_classes_dict
# # Basic solution without using the dict
# _a_class = ConcreteA
# _b_class = ConcreteB
class ConcreteD(AbstractD):
_classes_dict = _module_classes_dict
# # Basic solution without using the dict
# _a_class = ConcreteA
# _b_class = ConcreteB
# _c_class = ConcreteC
_module_classes_dict.update(a=ConcreteA, b=ConcreteB, c=ConcreteC, d=ConcreteD)
The issue is maybe not where you think it is.
Literally adding attributes seems really messy for more than a handful of classes.
I would be concerned if one of my classes was dependent on "more than a handful of classes". This is the issue, in my mind, you should try to solve.
Moreover, the mixin solution has a main drawback: ConcreteB knows about ConcreteC and ConcreteD whereas it should only know about ConcreteA. The dependencies between the classes are blurred. On the contrary, hard coding the dependencies should be a cleaner solution because the relationship between classes is explicit.
Hence this seems better than the mixin:
class ConcreteB(AbstractB):
_a_class = ConcreteA
class ConcreteC(AbstractC):
_a_class = ConcreteA
_b_class = ConcreteB
But sometimes hard coding the relations between ConcreteB and ConcreteA is not the best option. What if you want to use ConcreteA2 instead of ConcreteA?
class ConcreteA(AbstractA):
pass
class ConcreteA2(AbstractA):
pass
To make the code more versatile, you can use (as you wrote in a comment) the parameters of __init__:
class ConcreteB(AbstractB):
def __init__(self, a_class):
self._a_class = a_class
class ConcreteC(AbstractC):
def __init__(self, a_class, b_class):
self._a_class = a_class
self._b_class = b_class
But now, you might have an inconsistent set of classes:
b = ConcreteB(ConcreteA)
c = ConcreteC(ConcreteA2, ConcreteB)
This could happen if the codebase grows and the initialization of objects is dispatched across various modules. To avoid this situation, you may use a variant of the Factory Pattern:
class Factory:
def __init__(a_class, b_class, c_class, d_class):
self._a_class = a_class
self._b_class = b_class
self._c_class = c_class
def concreteA(self):
return self._a_class()
def concreteB(self):
return self._b_class(self._a_class)
def concreteC(self):
return self._c_class(self._a_class, self._c_class)
Now, you are sure that B and C share the same a_class.
This design helps you to ensure that the dependencies are explicit and consistent.
class Tesla_car:
def __init__(self,yourname):
self.name = yourname
print("Hey'%s',I am a bot and I will tell you about....." %self.name)
self.cells = self.batteries()
def material(self,model_no):
self.model = model_no
print("your car",self.model," made from aluminium")
def color(self,color):
self.color = color
print("the color of your car is:'%s'" %self.color)
class batteries:
def __init__(self):
pass
def materials(self):
self.battery_name = "Tesla tabless 4680 cells"
self.chemicals = "Tesla uses Lithium-Nickle-cobalt-magnesium(NMC) mixed in 8:1:1 ratio"
EV_car = Tesla_car('Blah')
EV_car()
Hey everyone, I am trying to use nested classes but whenever I try to use the inner class by writing self.cells = self.batteries() It raises an error:"Tesla_car' object has no attribute 'batteries"
How do I fix it
It seems that you're trying to compose objects, but in the wrong way.
Actually your classes reflect a perfect irl scenario for implementing composition: cars are equipped (composed) with a set of different objects, batteries included.
When using composition, you'd typically define TeslaCar and Batteries as separate classes, and then you would assign an instance of Batteries to one of TeslaCar instance variables. E.g.:
class Batteries:
def __init__(self):
...
class TeslaCar:
def __init__(self):
self.batteries = Batteries()
...
The above code is just a simple skeleton of how composition is implemented, but you can adapt it to your case very easily.
Finally FYI, avoid nesting classes at all. It's unpythonic and you'll discover that it's useless as soon as you dive deep into simple oop patterns like composition and inheritance.
Change
self.batteries()
to
Tesla_car.batteries()
your batteries inner class is wrongly indented.
Currently it is inside the color method instead of being at the same level as the method.
class TeslaCar:
def color(...):
...
class Batteries:
...
instead, do:
class TeslaCar:
def color(...):
...
class Batteries:
...
I have a class which contains a list like so:
class Zoo:
def __init__(self):
self._animals = []
I populate the list of animals with animal objects that have various properties:
class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length
You can imagine subclasses of Animal that have other properties. I want to be able to write methods that perform the same calculation but on different attributes of the Animal. For example, an average. I could write the following in Zoo:
def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)
That string lookup not only messes with my ability to document nicely, but using getattr seems odd (and maybe I'm just nervous passing strings around?). If this is good standard practice, that's fine. Creating get_average_speed(), get_average_height(), and get_average_length() methods, especially as I add more properties, seems unwise, too.
I realize I am trying to encapsulate a one-liner in this example, but is there a better way to go about creating methods like this based on properties of the objects in the Zoo's list? I've looked a little bit at factory functions, so when I understand them better, I think I could write something like this:
all_properties = ['speed', 'height', 'length']
for p in all_properties:
Zoo.make_average_function(p)
And then any instance of Zoo will have methods called get_average_speed(), get_average_height(), and get_average_length(), ideally with nice docstrings. Taking it one step further, I'd really like the Animal objects themselves to tell my Zoo what properties can be turned into get_average() methods. Going to the very end, let's say I subclass Animal and would like it to indicate it creates a new average method: (the following is pseudo-code, I don't know if decorators can be used like this)
class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length
#Zoo.make_average_function
#property
def tail_length(self):
return self._tail_length
Then, upon adding a Tiger to a Zoo, my method that adds animals to Zoo object would know to create a get_average_tail_length() method for that instance of the Zoo. Instead of having to keep a list of what average methods I need to make, the Animal-type objects indicate what things can be averaged.
Is there a nice way to get this sort of method generation? Or is there another approach besides getattr() to say "do some computation/work on an a particular property of every member in this list"?
Try this:
import functools
class Zoo:
def __init__(self):
self._animals = []
#classmethod
def make_average_function(cls, func):
setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
return func
def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)
class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length
class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length
#property
#Zoo.make_average_function
def tail_length(self):
return self._tail_length
my_zoo = Zoo()
my_zoo._animals.append(Tiger(10))
my_zoo._animals.append(Tiger(1))
my_zoo._animals.append(Tiger(13))
print(my_zoo.get_average_tail_length())
Note: If there are different zoos have different types of animals, it will lead to confusion.
Example
class Bird(Animal):
def __init__(self, speed):
self._speed = speed
#property
#Zoo.make_average_function
def speed(self):
return self._speed
my_zoo2 = Zoo()
my_zoo2._animals.append(Bird(13))
print(my_zoo2.get_average_speed()) # ok
print(my_zoo.get_average_speed()) # wrong
print(my_zoo2.get_average_tail_length()) # wrong
Question
How can you extend a python property?
A subclass can extend a super class's function by calling it in the overloaded version, and then operating on the result. Here's an example of what I mean when I say "extending a function":
# Extending a function (a tongue-in-cheek example)
class NormalMath(object):
def __init__(self, number):
self.number = number
def add_pi(self):
n = self.number
return n + 3.1415
class NewMath(object):
def add_pi(self):
# NewMath doesn't know how NormalMath added pi (and shouldn't need to).
# It just uses the result.
n = NormalMath.add_pi(self)
# In NewMath, fractions are considered too hard for our users.
# We therefore silently convert them to integers.
return int(n)
Is there an analogous operation to extending functions, but for functions that use the property decorator?
I want to do some additional calculations immediately after getting an expensive-to-compute attribute. I need to keep the attribute's access lazy. I don't want the user to have to invoke a special routine to make the calculations. basically, I don't want the user to ever know the calculations were made in the first place. However, the attribute must remain a property, since i've got legacy code I need to support.
Maybe this is a job for decorators? If I'm not mistaken, decorator is a function that wraps another function, and I'm looking to wrap a property with some more calculations, and then present it as a property again, which seems like a similar idea... but I can't quite figure it out.
My Specific Problem
I've got a base class LogFile with an expensive-to-construct attribute .dataframe. I've implemented it as a property (with the property decorator), so it won't actually parse the log file until I ask for the dataframe. So far, it works great. I can construct a bunch (100+) LogFile objects, and use cheaper methods to filter and select only the important ones to parse. And whenever I'm using the same LogFile over and over, i only have to parse it the first time I access the dataframe.
Now I need to write a LogFile subclass, SensorLog, that adds some extra columns to the base class's dataframe attribute, but I can't quite figure out the syntax to call the super class's dataframe construction routines (without knowing anything about their internal workings), then operate on the resulting dataframe, and then cache/return it.
# Base Class - rules for parsing/interacting with data.
class LogFile(object):
def __init__(self, file_name):
# file name to find the log file
self.file_name = file_name
# non-public variable to cache results of parse()
self._dataframe = None
def parse(self):
with open(self.file_name) as infile:
...
...
# Complex rules to interpret the file
...
...
self._dataframe = pandas.DataFrame(stuff)
#property
def dataframe(self):
"""
Returns the dataframe; parses file if necessary. This works great!
"""
if self._dataframe is None:
self.parse()
return self._dataframe
#dataframe.setter
def dataframe(self,value):
self._dataframe = value
# Sub class - adds more information to data, but does't parse
# must preserve established .dataframe interface
class SensorLog(LogFile):
def __init__(self, file_name):
# Call the super's constructor
LogFile.__init__(self, file_name)
# SensorLog doesn't actually know about (and doesn't rely on) the ._dataframe cache, so it overrides it just in case.
self._dataframe = None
# THIS IS THE PART I CAN'T FIGURE OUT
# Here's my best guess, but it doesn't quite work:
#property
def dataframe(self):
# use parent class's getter, invoking the hidden parse function and any other operations LogFile might do.
self._dataframe = LogFile.dataframe.getter()
# Add additional calculated columns
self._dataframe['extra_stuff'] = 'hello world!'
return self._dataframe
#dataframe.setter
def dataframe(self, value):
self._dataframe = value
Now, when these classes are used in an interactive session, the user should be able to interact with either in the same way.
>>> log = LogFile('data.csv')
>>> print log.dataframe
#### DataFrame with 10 columns goes here ####
>>> sensor = SensorLog('data.csv')
>>> print sensor.dataframe
#### DataFrame with 11 columns goes here ####
I have lots of existing code that takes a LogFile instance which provides a .dataframe attribute and dos something interesting (mostly plotting). I would LOVE to have SensorLog instances present the same interface so they can use the same code. Is it possible to extend the super-class's dataframe getter to take advantage of existing routines? How? Or am I better off doing this a different way?
Thanks for reading that huge wall of text. You are an internet super hero, dear reader. Got any ideas?
You should be calling the superclass properties, not bypassing them via self._dataframe. Here's a generic example:
class A(object):
def __init__(self):
self.__prop = None
#property
def prop(self):
return self.__prop
#prop.setter
def prop(self, value):
self.__prop = value
class B(A):
def __init__(self):
super(B, self).__init__()
#property
def prop(self):
value = A.prop.fget(self)
value['extra'] = 'stuff'
return value
#prop.setter
def prop(self, value):
A.prop.fset(self, value)
And using it:
b = B()
b.prop = dict((('a', 1), ('b', 2)))
print(b.prop)
Outputs:
{'a': 1, 'b': 2, 'extra': 'stuff'}
I would generally recommend placing side-effects in setters instead of getters, like this:
class A(object):
def __init__(self):
self.__prop = None
#property
def prop(self):
return self.__prop
#prop.setter
def prop(self, value):
self.__prop = value
class B(A):
def __init__(self):
super(B, self).__init__()
#property
def prop(self):
return A.prop.fget(self)
#prop.setter
def prop(self, value):
value['extra'] = 'stuff'
A.prop.fset(self, value)
Having costly operations within a getter is also generally to be avoided (such as your parse method).
If I understand correctly what you want to do is call the parent's method from the child instance. The usual way to do that is by using the super built-in.
I've taken your tongue-in-cheek example and modified it to use super in order to show you:
class NormalMath(object):
def __init__(self, number):
self.number = number
def add_pi(self):
n = self.number
return n + 3.1415
class NewMath(NormalMath):
def add_pi(self):
# this will call NormalMath's add_pi with
normal_maths_pi_plus_num = super(NewMath, self).add_pi()
return int(normal_maths_pi_plus_num)
In your Log example, instead of calling:
self._dataframe = LogFile.dataframe.getter()
you should call:
self._dataframe = super(SensorLog, self).dataframe
You can read more about super here
Edit: Even thought the example I gave you deals with methods, to do the same with #properties shouldn't be a problem.
You have some possibilities to consider:
1/ Inherit from logfile and override parse in your derived sensor class. It should be possible to modify your methods that work on dataframe to work regardless of the number of members that dataframe has - as you are using pandas a lot of it is done for you.
2/ Make sensor an instance of logfile then provide its own parse method.
3/ Generalise parse, and possibly some of your other methods, to use a list of data descriptors and possibly a dictionary of methods/rules either set in your class initialiser or set by a methods.
4/ Look at either making more use of the methods already in pandas, or possibly, extending pandas to provide the missing methods if you and others think that they would be accepted into pandas as useful extensions.
Personally I think that you would find the benefits of options 3 or 4 to be the most powerful.
The problem is that you're missing a self going into the parent class. If your parent is a singleton then a #staticmethod should work.
class X():
x=1
#staticmethod
def getx():
return X.x
class Y(X):
y=2
def getyx(self):
return X.getx()+self.y
wx = Y()
wx.getyx()
3
I have recently stated trying to use the newer style of classes in Python (those derived from object). As an excersise to familiarise myself with them I am trying to define a class which has a number of class instances as attributes, with each of these class instances describing a different type of data, e.g. 1d lists, 2d arrays, scalars etc. Essentially I wish to be able to write
some_class.data_type.some_variable
where data_type is a class instance describing a collection of variables. Below is my first attempt at implementing this, using just a profiles_1d instance and rather generic 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 roughly an appropriate way of tackling this problem. The examples I have seen of using property just set the value of some variable. Here I require my set method to initialise an instance of some class. If not, any other suggestions of better ways to implement this would be greatly appreciated.
In addition, is the way I am defining my set method ok? Generally the set method, as far as I understand, defines 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 type collection.set_profiles([...], y1=[...], ...), but I think that I shouldn't be directly calling this method. Ideally I would want to type collection.profiles = ([...], y1=[...], ...): is this correct/possible?
Finally, I have seen a decorators mentioned alot with repect to the new style of classes, but this is something I know very little about. Is the use of decorators appropriate here? Is this something I should know more about for this problem?
First, it's good you're learning new-style classes. They've got lots of advantages.
The modern way to make properties in Python is:
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 set profiles by doing
collection = Collection()
collection.profiles = (arg1, arg2, arg3), {'kwarg1':val1, 'kwarg2':val2}
Notice all three methods having the same name.
This is not normally done; either have them pass the attributes to collections constructor or have them create the profiles_1d themselves and then do collections.profiles = myprofiles1d or pass it to the constructor.
When you want the attribute to manage access to itself instead of the class managing access to the attribute, make the attribute a class with a descriptor. Do this if, unlike in the property example above, you actually want the data stored inside the attribute (instead of another, faux-private instance variable). Also, it's good for if you're going to use the same property over and over again -- make it a descriptor and you don't need to write the code multiple times or use a base class.
I actually like the page by #S.Lott -- Building Skills in Python's Attributes, Properties and Descriptors.
When creating propertys (or other descriptors) that need to call other instance methods the naming convention is to prepend an _ to those 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 don't have to create the (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 on the class instead of instances -- if this is so, properties on the class won't work as collections.profiles would be overridden with a profiles_1d object, clobbering the property... if this is really what you want, you'll have to make a metaclass and put the property there instead.
Hopefully you are talking about instances, so the class would look like:
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 would do something like:
collection = Collection()
collection.profiles1d = x, y1, y2, y3
A couple things to note: the setter method gets called with only two items: self, and the new value (which is why you were having to call set_profiles1d manually); when doing an assignment, keyword naming is not an option (that only works in function calls, which an assignment is not). If it makes sense for you, you can get fancy and do something like:
collection.profiles1d = (x, dict(y1=y1, y2=y2, y3=y3))
and then change the setter to:
#profiles1d.setter
def profiles1d(self, value):
x, y = value
self._profiles_1d = profiles_1d(x, **y)
which is still fairly readable (although I prefer the x, y1, y2, y3 version myself).