Is this sound software engineering practice for class construction? - python

Is this a plausible and sound way to write a class where there is a syntactic sugar #staticmethod that is used for the outside to interact with? Thanks.
###scrip1.py###
import SampleClass.method1 as method1
output = method1(input_var)
###script2.py###
class SampleClass(object):
def __init__(self):
self.var1 = 'var1'
self.var2 = 'var2'
#staticmethod
def method1(input_var):
# Syntactic Sugar method that outside uses
sample_class = SampleClass()
result = sample_class._method2(input_var)
return result
def _method2(self, input_var):
# Main method executes the various steps.
self.var4 = self._method3(input_var)
return self._method4(self.var4)
def _method3(self):
pass
def _method4(self):
pass

Answering to both your question and your comment, yes it is possible to write such a code but I see no point in doing it:
class A:
def __new__(cls, value):
return cls.meth1(value)
def meth1(value):
return value + 1
result = A(100)
print(result)
# output:
101
You can't store a reference to a class A instance because you get your method result instead of an A instance. And because of this, an existing __init__will not be called.
So if the instance just calculates something and gets discarded right away, what you want is to write a simple function, not a class. You are not storing state anywhere.
And if you look at it:
result = some_func(value)
looks exactly to what people expect when reading it, a function call.
So no, it is not a good practice unless you come up with a good use case for it (I can't remember one right now)
Also relevant for this question is the documentation here to understand __new__ and __init__ behaviour.
Regarding your other comment below my answer:
defining __init__ in a class to set the initial state (attribute values) of the (already) created instance happens all the time. But __new__ has the different goal of customizing the object creation. The instance object does not exist yet when __new__is run (it is a constructor function). __new__ is rarely needed in Python unless you need things like a singleton, say a class A that always returns the very same object instance (of A) when called with A(). Normal user-defined classes usually return a new object on instantiation. You can check this with the id() builtin function. Another use case is when you create your own version (by subclassing) of an immutable type. Because it's immutable the value was already set and there is no way of changing the value inside __init__ or later. Hence the need to act before that, adding code inside __new__. Using __new__ without returning an object of the same class type (this is the uncommon case) has the addtional problem of not running __init__.
If you are just grouping lots of methods inside a class but there is still no state to store/manage in each instance (you notice this also by the absence of self use in the methods body), consider not using a class at all and organize these methods now turned into selfless functions in a module or package for import. Because it looks you are grouping just to organize related code.
If you stick to classes because there is state involved, consider breaking the class into smaller classes with no more than five to 7 methods. Think also of giving them some more structure by grouping some of the small classes in various modules/submodules and using subclasses, because a long plain list of small classes (or functions anyway) can be mentally difficult to follow.
This has nothing to do with __new__ usage.
In summary, use the syntax of a call for a function call that returns a result (or None) or for an object instantiation by calling the class name. In this case the usual is to return an object of the intended type (the class called). Returning the result of a method usually involves returning a different type and that can look unexpected to the class user. There is a close use case to this where some coders return self from their methods to allow for train-like syntax:
my_font = SomeFont().italic().bold()
Finally if you don't like result = A().method(value), consider an alias:
func = A().method
...
result = func(value)
Note how you are left with no reference to the A() instance in your code.
If you need the reference split further the assignment:
a = A()
func = a.method
...
result = func(value)
If the reference to A() is not needed then you probably don't need the instance too, and the class is just grouping the methods. You can just write
func = A.method
result = func(value)
where selfless methods should be decorated with #staticmethod because there is no instance involved. Note also how static methods could be turned into simple functions outside classes.
Edit:
I have setup an example similar to what you are trying to acomplish. It is also difficult to judge if having methods injecting results into the next method is the best choice for a multistep procedure. Because they share some state, they are coupled to each other and so can also inject errors to each other more easily. I assume you want to share some data between them that way (and that's why you are setting them up in a class):
So this an example class where a public method builds the result by calling a chain of internal methods. All methods depend on object state, self.offset in this case, despite getting an input value for calculations.
Because of this it makes sense that every method uses self to access the state. It also makes sense that you are able to instantiate different objects holding different configurations, so I see no use here for #staticmethod or #classmethod.
Initial instance configuration is done in __init__ as usual.
# file: multistepinc.py
def __init__(self, offset):
self.offset = offset
def result(self, value):
return self._step1(value)
def _step1(self, x):
x = self._step2(x)
return self.offset + 1 + x
def _step2(self, x):
x = self._step3(x)
return self.offset + 2 + x
def _step3(self, x):
return self.offset + 3 + x
def get_multi_step_inc(offset):
return MultiStepInc(offset).result
--------
# file: multistepinc_example.py
from multistepinc import get_multi_step_inc
# get the result method of a configured
# MultiStepInc instance
# with offset = 10.
# Much like an object factory, but you
# mentioned to prefer to have the result
# method of the instance
# instead of the instance itself.
inc10 = get_multi_step_inc(10)
# invoke the inc10 method
result = inc10(1)
print(result)
# creating another instance with offset=2
inc2 = get_multi_step_inc(2)
result = inc2(1)
print(result)
# if you need to manipulate the object
# instance
# you have to (on file top)
from multistepinc import MultiStepInc
# and then
inc_obj = MultiStepInc(5)
# ...
# ... do something with your obj, then
result = inc_obj.result(1)
print(result)
Outputs:
37
13
22

Related

Function to behave differently on class vs on instance

I'd like a particular function to be callable as a classmethod, and to behave differently when it's called on an instance.
For example, if I have a class Thing, I want Thing.get_other_thing() to work, but also thing = Thing(); thing.get_other_thing() to behave differently.
I think overwriting the get_other_thing method on initialization should work (see below), but that seems a bit hacky. Is there a better way?
class Thing:
def __init__(self):
self.get_other_thing = self._get_other_thing_inst()
#classmethod
def get_other_thing(cls):
# do something...
def _get_other_thing_inst(self):
# do something else
Great question! What you seek can be easily done using descriptors.
Descriptors are Python objects which implement the descriptor protocol, usually starting with __get__().
They exist, mostly, to be set as a class attribute on different classes. Upon accessing them, their __get__() method is called, with the instance and owner class passed in.
class DifferentFunc:
"""Deploys a different function accroding to attribute access
I am a descriptor.
"""
def __init__(self, clsfunc, instfunc):
# Set our functions
self.clsfunc = clsfunc
self.instfunc = instfunc
def __get__(self, inst, owner):
# Accessed from class
if inst is None:
return self.clsfunc.__get__(None, owner)
# Accessed from instance
return self.instfunc.__get__(inst, owner)
class Test:
#classmethod
def _get_other_thing(cls):
print("Accessed through class")
def _get_other_thing_inst(inst):
print("Accessed through instance")
get_other_thing = DifferentFunc(_get_other_thing,
_get_other_thing_inst)
And now for the result:
>>> Test.get_other_thing()
Accessed through class
>>> Test().get_other_thing()
Accessed through instance
That was easy!
By the way, did you notice me using __get__ on the class and instance function? Guess what? Functions are also descriptors, and that's the way they work!
>>> def func(self):
... pass
...
>>> func.__get__(object(), object)
<bound method func of <object object at 0x000000000046E100>>
Upon accessing a function attribute, it's __get__ is called, and that's how you get function binding.
For more information, I highly suggest reading the Python manual and the "How-To" linked above. Descriptors are one of Python's most powerful features and are barely even known.
Why not set the function on instantiation?
Or Why not set self.func = self._func inside __init__?
Setting the function on instantiation comes with quite a few problems:
self.func = self._funccauses a circular reference. The instance is stored inside the function object returned by self._func. This on the other hand is stored upon the instance during the assignment. The end result is that the instance references itself and will clean up in a much slower and heavier manner.
Other code interacting with your class might attempt to take the function straight out of the class, and use __get__(), which is the usual expected method, to bind it. They will receive the wrong function.
Will not work with __slots__.
Although with descriptors you need to understand the mechanism, setting it on __init__ isn't as clean and requires setting multiple functions on __init__.
Takes more memory. Instead of storing one single function, you store a bound function for each and every instance.
Will not work with properties.
There are many more that I didn't add as the list goes on and on.
Here is a bit hacky solution:
class Thing(object):
#staticmethod
def get_other_thing():
return 1
def __getattribute__(self, name):
if name == 'get_other_thing':
return lambda: 2
return super(Thing, self).__getattribute__(name)
print Thing.get_other_thing() # 1
print Thing().get_other_thing() # 2
If we are on class, staticmethod is executed. If we are on instance, __getattribute__ is first to be executed, so we can return not Thing.get_other_thing but some other function (lambda in my case)

How to best initialize an object of a subclass using an object from the parent class?

I want to create a subclass of a class of an existing package (whose source code I don't want to/cannot change). The objects of the class are initialized just using a string and then populated later on using all kind of add functions. A minimal example could look like this (without any add functions):
import copy
class Origin(object):
def __init__(self, name):
self.name = name
self.dummy_list = [1, 2, 'a']
self.dummy_stuff = {'a': [12, 'yt']}
def make_copy(self):
return copy.deepcopy(self)
def dummy_function(self):
return len(self.dummy_list)
I want to create a subclass in such a way that I can initialize its instances using an instance of Origin. A straightforward way would be
class BasedOnOrigin(Origin):
def __init__(self, origin_instance, new_prop):
Origin.__init__(self, origin_instance.name)
self.dummy_list = copy.deepcopy(origin_instance.dummy_list)
self.dummy_stuff = copy.deepcopy(origin_instance.dummy_stuff)
self.new_prop = new_prop
The annoying thing there is, that I need to copy all kind of things which I need to know about in advance.
Another option would be
class BasedOnOrigin2(Origin):
def __init__(self, origin_instance, new_prop):
Origin.__init__(self, origin_instance.name)
self = origin_instance.make_copy()
self.new_prop = new_prop
but the self = part looks rather non-standard and new_prop is not set, so I would need an extra function for this.
Is there a standard way of doing this?
An alternative to the above would be to add the additional functions to existing instances using e.g.
from functools import partial
def add_function(obj, func):
setattr(obj, func.__name__, partial(func, obj))
but this can be annoying if there are (i) a lot of functions to add and (ii) a lot of instances to which one wants to add functions.
but the self = part looks rather non-standard and new_prop is not set
self is just a plain local variable, so rebinding it only effects the local scope indeed.
Is there a standard way of doing this?
From what you describe it looks like your real problem is that you have instances of class created by another lib that you don't want / cannot modify and what you really want is to add new methods (and eventually override some methods) to those objects, but cannot since you can tell this lib to use your own class instead.
If the point is purely and simply "replace" the original class with your own version of it (so all instances of the original class are impacted by the change), the canonical solution is to monkeypatch the original class:
from otherlib import TheClass
def patch_the_class():
# we do this in a function to avoid
# polluting the global namespace
# add a new method
def newmethod(self):
# code here
TheClass.newmethod = newmethod
# override an existing method
# keep a reference to the original so
# we can still use it:
_original = TheClass.some_method
def mymethod(self, arg):
something = _original(self, arg)
# additional stuff here
return something
TheClass.some_method = mymethod
patch_the_class()
Just make sure this is executed before any use of the patched class and you're done.
The pro of this solution (wrt/ patching each instance individually) is a lesser cost and the assurance that no one will ever forget to patch an instance.
Now note that monkeypatches are to be considered as either a temporary workaround or a last-resort hack. If the lib you are patching is OSS, you can modify it to either improve the original class or implement some way to make the concrete class to use configurable and contribute it back.
I think the best approach is defining a function that will extend original origin instance without copying it e.g.
def exdend(*origin_instances):
def my_function_one(self):
pass
def my_function_two(self):
pass
for origin_instance in origin_instances:
setattr(origin_instance, my_function_one.__name__, partial(my_function_one, origin_instance))
setattr(origin_instance, my_function_two.__name__, partial(my_function_two, origin_instance))
return origin_instances

How and where to compute derived instance variables in python

I have a class with a str instance variable. From this instance variable, I calculate a second instance variable, which is basically just the string broken up into certain 'atoms'. The second instance variable is completely determined by the first. I've made it an instance variable because I think that it is best regarded as a 'property' of the class. I'm a bit unsure about how to treat derived instance variables. In particular:
1) I think that they should be get-only properties. However, since the computation of the derived instance variable is quite intensive, I want it to be done when the class is initiated, not when the variable is called.
2) If I make a function purely for calculating the instance variable, is there a way to mark this?
3) Also, should I pass the first instance variable as a parameter, or just read it in the method from self? (in general I'm still a bit unsure of when to pass instance variables as parameters to methods.)
4) Is there a better way to do this that I haven't mentioned?
Thanks
EDIT: Here's a simplified example of what I mean:
class Amendment:
def __init__(self, string):
self.string = string
self.atoms = generate_atoms()
def generate_atoms():
return do_something_that_takes_long(self.string)
You forgot self in a couple of places. But here's how to make .string and .atoms get-only properties. We use a couple of "private" attributes that are created during __init__, and use #property to create the actual getters.
class Amendment:
def __init__(self, string):
self._string = string
self._atoms = self.generate_atoms()
def generate_atoms(self):
#return do_something_that_takes_long(self.string)
return list(self.string)
#property
def string(self):
return self._string
#property
def atoms(self):
return self._atoms
# Test
a = Amendment('abc')
print(a.string, a.atoms)
# This will raise an error because `.string` is a get-only property.
a.string = 'xyz'
output
abc ['a', 'b', 'c']
Traceback (most recent call last):
File "./qtest.py", line 53, in <module>
a.string = 'xyz'
AttributeError: can't set attribute
If you like, you could also mark generate_atoms as private, but there's probably no need. And nothing stops an insistent user from accessing such things anyway, as the linked docs explain.
As for your 3rd question, methods should normally access the attributes they need via self. In some cases you can use the same method on different attributes, and then it makes sense to pass the attribute as a parameter, but if that's not the case it just looks weird. ;)

Using decorators in python to build a complex composite class from a set of simple component classes

I am using the decorator design pattern to build a "composite class" that composes together the behavior of a set of "component classes". The behavior of the relevant method from each component class is governed by a dictionary param_dict, so that each component class has its own param_dict. The composite class also has a composite_param_dict, which is successively built up from the component dictionaries.
The behavior I need is the following: when an instance of the composite class has a value of composite_param_dict changed, I need the behavior of the inherited method to change.
Here is a minimal example of my class design:
class Component(object):
def __init__(self):
self.param_dict = {'a':4}
def component_method(self, x):
return self.param_dict['a']*x
I pass an instance of Component to the Composite constructor:
class Composite(object):
def __init__(self, instance):
self.instance = instance
# In the following line of code,
# I use copy to emphasize that there actually multiple
# instance.param_dict that are being passed to init,
# so composite_param_dict is not simply a reference
self.composite_param_dict = copy(self.instance.param_dict)
setattr(self, 'composite_method', self.param_dict_decorator(getattr(self.instance, 'component_method')))
def param_dict_decorator(self, func):
self.instance.param_dict['a'] = self.composite_param_dict['a']
return func
For the sake of being concise in this example, there is only one component, but in general there are many, so in general composite_param_dict has many keys, and the composite class has many inherited methods.
Additionally, I need to use getattr and setattr because I will not necessarily know in advance what the names of the methods I will need to inherit are. In general, the component models will tell the composite model which methods to inherit, so I cannot hard-code the method names into the composite model. In this minimal example, for the sake of being concise, I have gone ahead and hard-coded the method name component_method, and suppressed the mechanism by which this information is transmitted.
I build my composite class as follows:
component_instance = Component()
composite_instance = Composite(component_instance)
With my decorator written as I have in the above example, changes in the composite_param_dict do not propagate correctly, but I do not understand why not. For example:
composite_instance.composite_param_dict['a'] = 10
print composite_instance.composite_method(10)
40
If the values of composite_param_dict were correctly propagating, then the correct answer should be 100.
You only call param_dict_decorator once, at the moment when you create the composite_method. It is not called again every time you call the composite method. So it effectively "freezes" self.instance.param_dict with the value present in self.composite_param_dict at the time when you create the composite object.
If you want custom code to run every time composite_method is called, you can't just return func from param_dict_decorator. param_dict_decorator is only called once; it is what is returned from param_dict_decorator that you assign to composite_method, so that is what will be called whenever you call composite_method. So you need param_dict_decorator to return a new function that incorporates the "updating" behavior. Here's an example:
def param_dict_decorator(self, func):
def wrapper(*args, **kw):
self.instance.param_dict['a'] = self.composite_param_dict['a']
return func(*args, **kw)
return wrapper
With this change, it works:
>>> composite_instance = Composite(component_instance)
>>> composite_instance.composite_method(10)
40
>>> composite_instance.composite_param_dict['a'] = 10
>>> composite_instance.composite_method(10)
100
More generally, the concept of decorators is that they take in a function and return a new function that is meant to replace the original function. In your param_dict_decorator, you just return the original function, so your decorator has no effect at all on the behavior of func.

Difficulties with re-using a variable

here is a part of my code :
class projet(object):
def nameCouche(self):
valLissage = float(ui.valLissage.displayText())
return (valLissage)
valCouche = nameCouche() # asks for a positional argument but 'self' doesnt work
def choixTraitement(self):
ui.okLissage.clicked.connect(p.goLissage)
def goLissage(self, valCouche):
if ui.chkboxLissage.isChecked():
print(valCouche) # result is False
os.system(r'"C:\Program Files\FME\fme.exe" D:\Stelios\..... --MAX_NUM_POINTS {0}'.format(valCouche))
So I would like to use valCouche in goLissage method but it doesnt work.
I thought that valCouche would have the argument of valLissage but instead it gives False as a value.
I've tried different alternatives but still doesnt work.
You've got multiple problems here.
First, if you write this in the middle of a class definition:
valCouche = nameCouche()
... you're creating a class attribute, which is shared by all instances, not a normal instance attribute.
Also, you're running this at class definition time. That means there is no self yet--there aren't any instances yet to be self--so you can't call a method like nameCouche, because you don't have anything to call it on.
What you want to do is call the method at instance initialization time, on the instance being initialized, and store the return value in an instance attribute:
def __init__(self):
self.valCouche = self.nameCouche()
Then, when you want to access this value in another method later, you have to access it as self.valCouche.
If you make those changes, it will work. But your object model still doesn't make much sense. Why is nameCouche a method when it doesn't have anything to do with the object, and doesn't access any of its attributes? Maybe it makes sense as a #staticmethod, but really, I think it makes more sense just as a plain function outside the class. In fact, none of the code you've written seems to have anything to do with the class.
This kind of cram-everything-into-the-class design is often a sign that you're trying to write Java code in Python, and haven't yet really understood how Python does OO. You might want to read a good tutorial on Python classes. But briefly: if you're writing a class just to have somewhere to dump a bunch of vaguely-related functions, what you want is a module, not a class. If you have some reason to have instances of that class, and the functions all act on the data of each instance, then you want a class.
You have to declare variabile in the __init__ method (constructor) and then use it in your code
ex:
class projet(object):
def __init__(self):
self.valCouche = ''
def nameCouche(self):
valLissage = float(ui.valLissage.displayText())
return (valLissage)
def choixTraitement(self):
ui.okLissage.clicked.connect(p.goLissage)
def goLissage(self, valCouche):
if ui.chkboxLissage.isChecked():
self.valCouche = self.nameCouche()
print(self.valCouche) # result is False
os.system(r'"C:\Program Files\FME\fme.exe" D:\Stelios\..... --MAX_NUM_POINTS {0}'.format(self.valCouche))
you have to define an initialization function: def__init__(self)
defining valCouche as an instance attribute make it accessible on all the method so we have the following
class projet(object):
def __init__(self):
self.valCouche = ''
def nameCouche(self):
self.valCouche = float(ui.valLissage.displayText())
#staticmethod #here there is no need for self so it is a method of class
def choixTraitement():
ui.okLissage.clicked.connect(p.goLissage)
def goLissage(self):
if ui.chkboxLissage.isChecked():
print(self.valCouche) # result is False
os.system(r'"C:\Program Files\FME\fme.exe" D:\Stelios\..... --MAX_NUM_POINTS {0}'.format(self.valCouche))

Categories