Convention for inheritance from class instance to add details/functionality - python

I have a class called Image
class Image(object):
def __init__(self,rgb_img):
self.img_time = time.time()
self.img_colr = rgb_img
I want to store some more information on this class at a later time (image keypoints, grey scale, etc.) and believe it is more clear to make a new class altogether. But I don't know the best practice for this. Heres my guess:
img = Image(rgb_img)
class DetailedImage(object):
def __init__(self, img, kp, grey):
self = img
self.kp = kp
self.grey = grey
Still, this option is implicit. It is not immediately obvious that the img argument must be an Image instance.
What is the best practice for creating a new class using an instance from another class? (i.e. if I have a peasant, and he trains to become a warrior, how to I pass information from the instantiated peasant class to a separate warrior class?) Is this even a good idea to do?

As I've said in the comment: assigning to self makes me cringe, don't do that.
Apart from that you'll have many options how to get what you need.
Use composition:
class DetailedImage(object):
def __init__(self, img):
"""
:param Image img: Internal image
"""
self.img = img
In this case I specified parameter type using docstrings in sphinx format.
Use normal python inheritance.
Python inheritance (even multiple-inheritance) is usefull, and better than in other languages. I'd rathere refer you to the docs, than write too long answer. Or see this: How does Python's super() work with multiple inheritance?.

You can simply add any attribute to python class instances, you don't need to define a special class:
img = Image(rgb_img)
img.kp = kp
img.grey = grey

Related

Variables inside constructor without "self" - Python

I am trying to understand whether declaring variables inside constructors is an ok practice. I only need these variables inside the constructor. I am asking this because most of the times I've seen constructors they only contained self variables. I've tried to find an answer on the internet but had no luck.
Here is an example code
class Patient:
def __init__(self, all_images_path: str):
all_patient_images = os.listdir(all_images_path)
all_patient_images = [(all_images_path + x) for x in all_patient_images if 'colourlay' in x]
self.images: [Image] = [Image.open(img_path) for img_path in all_patient_images]
Is there anything wrong with the above code? If yes, what?
Thank you!
__init__ is just a normal function that has a special purpose (initializing the newly created object).
You can do (almost) whatever you like in there. You typically see lines like self.name = arg because assignments like these are often required to set up the internal state of the object. It's perfectly fine to have local variables though, and if you find they make your code cleaner, use them.
From a design standpoint, Patient.__init__ is doing too much. I would keep __init__ as simple as possible:
class Patient:
def __init__(self, images: list[Image]):
self.images = images
The caller of __init__ is responsible for producing that list of Image values. However, that doesn't mean your user is the one calling __init__. You can define a class method to handle the messy details of extracting images from a path, and calling __init__. (Note it gets less messy if you use the pathlib module.)
from pathlib import Path
class Patient:
def __init__(self, images: list[Image]):
self.images = images
#classmethod
def from_path(cls, all_images_path: Path):
files = all_images_path.glob('*colourlay*')
return cls([Image.open(f) for f in files])
Note that Image itself seems to take this approach: you aren't constructing an instance of Image directly, but using a class method to extract the image data from a file.

Class Method; returning given input

I have the code:
class Triangle(Coordinate):
def __init__(self,xcoord,ycoord,color):
self.color = color
super().__init__(xcoord,ycoord)
(inheriting from class Coordinate)
And whenever I call it, for example
t1 = Triangle(Coordinate(1,1), Coordinate(2,1), Red)
It only returns the coordinates, how do it make it return the color as well?
The answer has been posted in the comments but I think you misunderstood him, so I am going to elaborate a bit more.
Your class Triangle inherits functions from a parents class of some sort. Even though you didn't write a def __str__(self): in Triangle, it can still be used because it is defined in your parent class, and Triangle inherits functions from the parent class
If you don't want to use any of the parent class' functions, you can redefine it in Triangle. When you call that function on a Triangle Python will prefer to use the def __str__(self): in Triangle before referencing the parent class.
In short, you need to write a new def __str__(self): similar to the one in your parent class, but add another portion in the output that accounts for the color. I hope this helps Charmaine. Good luck!

Can I add methods and attributes to a class derived from peewee.Model

I am working on an app with Cherrypy and Peewee, and I would like to know if my approach is good or wrong and dangerous.
All the examples of Peewee classes that I found have only the Meta subclass and the <xxx>Field attributes. I have never found an example with helper methods or properties.
I tried to make my own, and it works!
It works very well and I find it convenient, but I am afraid that I'm getting into trouble.
So my question is: can I add as many properties and methods to my classes derived from the peewee.Model, so I can do cool things like in the snippet below?
Or are there limits and guidelines to what I can do?
class PeeweeModel(peewee.Model):
class Meta:
database = db
class TempFile(PeeweeModel):
file_type = peewee.IntegerField()
original_file_name = peewee.CharField()
temp_file_name = peewee.CharField()
DRAWING = 1
PDF = 2
def href(self, settings):
if(self.file_type == DRAWING:
return self.temp_file_name
elif(self.file_type == PDF:
if settings.show_pdf:
return self.temp_file_name
elif settings.show_bitmap:
bitmap = TempFile.create_bitmap_from_pdf(self.temp_file_name)
return bitmap.temp_file_name
#staticmethod
def create_bitmap_from_pdf(self, file_name):
[...]
Edit:
I would appreciate a comment about the constructor.
For example, I would like to do both these:
tmp = TempFile(file_name) # and run my own constructor
tmp = TempFile(original_file_name=file_name, [...]) # and run Peewee's constructor
Yes! Of course you can, by all means. Models are just normal python classes, so you can treat them as such and add methods, properties, attributes, etc.

calling private methods for class method: python

I am trying to implement multiple constructors in python and one of the suggestions (through online searching) was to use the classmethod. However, using this, I am having issues with code reuse and modularity. Here is an example where I can create an object based on a supplied file or through some other means:
class Image:
def __init__(self, filename):
self.image = lib.load(filename)
self.init_others()
#classmethod
def from_data(cls, data, header):
cls.image = lib.from_data(data, header)
cls.init_others()
return cos
def init_others(self):
# initialise some other variables
self.something = numpy.matrix(4,4)
Now it seems that I cannot do that. The cls.init_others() call fails by saying that I have not provided the object to call it on. I guess I can initialise things in the from_data function itself but then I repeat the code in the init method and the other "constructors". Does anyone know how I can call these other initialiser methods from these #classmethod marked functions? Or perhaps someone knows a better way to initialise these variables.
I come from a C++ background. So still trying to find my way around the python constructs.
Your class method should create and return a new instance of the class, not assign class attributes and return the class itself. As an alternative to the keyword arguments, you could do something like:
class Image:
def __init__(self, image):
self.image = image
self.init_others()
#classmethod
def from_data(cls, data, header):
return cls(lib.from_data(data, header))
#classmethod
def from_filename(cls, filename):
return cls(lib.load(filename))
def init_others(self):
# initialise some other variables
self.something = numpy.matrix(4, 4)
This adds the ability to create an instance if you already have the image, too.
I would recommend not trying to create multiple constructors, and use keyword arguments instead:
class Image(object):
def __init__(self, filename=None, data=None, header=None):
if filename is not None:
self.image = lib.load(filename)
elif data is not None and header is not None:
self.image = lib.from_data(data, header)
else:
raise ValueError("You must provide filename or both data and header")
self.init_others()
def init_others(self):
# initialise some other variables
self.something = numpy.matrix(4,4)
This is a more Pythonic way to handle this scenario.
You should always pass in self as the first argument to any method that will act on a class instance. Python will not automatically determine the instance you're trying to call the method for unless you do that. So if you want to use a class function like
the_image = Image("file.txt")
the_image.interpolate(foo,bar)
You need to define the method within Image as
def interpolate(self,foo,bar):
# Your code

Can I treat an instance of one class as an attribute of another

Ideally I would like to extend an instance of PIL's image class to include some custom methods; Image doesn't support this however, so I need to change tack. Delegation seems to be the most conventional alternative, but before I stumbled upon this aspect of python I had toyed with the idea of creating my custom class with an attribute that functioned as an instance of the PIL Image class. I've not seen this done anywhere else, but is this essentially the same thing as delegation?
import Image
MyImageClass():
def __init__(self, filepath):
self.attrA = ...
self.attrB = ...
self.attrc = ...
self.img = Image.open(filepath)
def method_A(self):
...
def method_B(self):
im = MyImageClass('/path/to/png')
im.self.img.thumbnail((100,100))
Sure. This is no different than:
class Foo(object):
def __init__(self):
self.bar = 'test'
im = Foo()
print im.bar.upper()
Notice that it is im.bar, not im.self.bar.
self in __init__ is the same as im so self.bar in __init__ is the same as im.bar outside of it.
Of course instances can be attributes of classes. But you say that you want an enhanced Image class - so extend Image:
#take this with a grain of salt - I didn't test the code
from PIL.Image import Image
MyImageClass(Image):
def __init__(self):
super(MyImageClass, self).__init__()
self.init_some = 'custom_stuff'
def method_A(self):
...
def method_B(self):
im = MyImageClass.open('/path/to/png')
im.thumbnail((100,100))
Update
As OP pointed out Image isn't designed to be subclassed by application code. Hence my example is just a general example for inheritance.
In Python, the boundaries beween delegation, composition, aggregation, and other has a repationships are pretty blurry, as they all tend to share the same syntax, and with garbage collection there are no worries about ownership. What you call it will mainly depend on what you do with the objects, i.e. if MyImageClass.method_A just calls self.img.method_A(), etc., it would be delegation.
I'm pretty sure you have seen this before, as every value in Python – including values of all attributes – is an instance of some type.
If your class is an Image, you're looking for inheritance. Your class should be a subclass of Image.

Categories