A class is more or less a fancy wrapper for a dict of attributes to objects. When you instantiate a class you can assign to its attributes, and those will be stored in foo.__dict__; likewise, you can look in foo.__dict__ for any attributes you have already written.
This means you can do some neat dynamic things like:
class Employee: pass
def foo(self): pass
Employee.foo = foo
as well as assigning to a particular instance. (EDIT: added self parameter)
A class is more or less a fancy wrapper for a dict of attributes to objects. When you instantiate a class you can assign to its attributes, and those will be stored in foo.__dict__; likewise, you can look in foo.__dict__ for any attributes you have already written.
This means you can do some neat dynamic things like:
class Employee: pass
def foo(self): pass
Employee.foo = foo
as well as assigning to a particular instance. (EDIT: added self parameter)
Try with lambda:
john.greet = lambda : print( 'hello world!' )
The you'll be able to do:
john.greet()
EDIT: Thanks Thomas K for the note - this works on Python 3.2 and not for Python2, where print appeared to be statement. But this will work for lambdas, without statements (right? Sorry, I know only python3.2 (: )
Videos
When you create a new class, you are creating a new type. This newly created type may have some properties, or may not. These properties allow to hold data, methods etc.
Empty class that doesn't inherit any base class would be useful as placeholder.
class ServiceWrapper(object):
pass
def sendMessage(svc:ServiceWrapper):
#do something
#pass
On the other hand, empty classes that inherit other classes is a very common pattern. Specially when defining user exceptions.
class Networkerror(RuntimeError):
pass
try:
raise Networkerror()
except Networkerror:
#do something
#pass
Also recently, the python collections.abc allows creating interface like functionalities.
class ServiceWrapper(ABC):
@abstractmethod
def send(self):...
def sendMessage(svc:ServiceWrapper):
svc.send()
#pass
I recently found myself using empty Python classes as unique "tags" for an observer system.
For example, something like this...
from collections import defaultdict
class OnCreate:
pass
class OnModify:
pass
class OnDelete:
pass
class ObserverSystem:
def __init__(self):
self.observers = defaultdict(list)
def register(self, event, callback):
self.observers[event].append(callback)
def notify(self, event, *args, **kwargs):
for callback in self.observers[event]:
callback(*args, **kwargs)
observer = ObserverSystem()
observer.register(OnCreate, lambda entity: print(f"Entity {entity} created"))
observer.register(OnModify, lambda entity: print(f"Entity {entity} modified"))
observer.register(OnDelete, lambda entity: print(f"Entity {entity} deleted"))
observer.notify(OnCreate, 1)
observer.notify(OnModify, 2)
observer.notify(OnDelete, 3)
I guess, I could have used a of numeric value, or even a string as the "tag", but the class is unique, it's hashable so I can use it as dict key, etc. Seems to work well.
This is just fine:
class Parent:
# the __init__ is inherited from parent
pass
class Child(Parent):
# the __init__ is inherited from parent
pass
This is also fine:
class Parent:
# the __init__ is inherited from parent
pass
class Child(Parent):
def __init__(self):
# __init__ is called on parent
super().__init__()
This may seem ok, and will usually work fine, but not always:
class Parent:
# the __init__ is inherited from parent
pass
class Child(Parent):
def __init__(self):
# this does not call parent's __init__,
pass
Here is one example where it goes wrong:
class Parent2:
def __init__(self):
super().__init__()
print('Parent2 initialized')
class Child2(Child, Parent2):
pass
# you'd expect this to call Parent2.__init__, but it won't:
Child2()
This is because the MRO of Child2 is: Child2 -> Child -> Parent -> Parent2 -> object.
Child2.__init__ is inherited from Child and that one does not call Parent2.__init__, because of the missing call to super().__init__.
No it isn't necessary. It is necessary when you want the parent's logic to run as well.
class Parent:
def __init__(self):
self.some_field = 'value'
class Child(Parent):
def __init__(self):
self.other_field = 'other_value'
super().__init__()
child = Child()
child.some_field # 'value'
Note: This post contains two different implementation techniques to allow for what you want.
Solution Through Indirection
The easiest way around this issue is to refactor the code so that the child classes does not directly override the function used by the public interface.
Instead provide the public functionality directly in the base-class, and make children override a "worker" (implementation detail) of said function that is later called by the function invoked "from the outside".
Example Implementation
class Base (object):
def get_message (self):
try:
return self.get_message_impl ()
except Exception as detail:
print ("error:", detail)
return None
def get_message_impl (self):
raise Exception ("Not Implemented")
class Foo (Base):
def get_message_impl (self):
return "Hello World";
class Bar (Base):
def get_message_impl (self):
raise Exception ("Bar.get_message_impl always fails!")
f = Foo ()
b = Bar ()
f_msg = f.get_message ()
b_msg = b.get_message ()
print ("f_msg:", f_msg)
print ("b_msg:", b_msg)
output
error: Bar.get_message_impl always fails!
f_msg: Hello World
b_msg: None
opt-in protection
If you would like to maintain the possibility to override the public functionality presented in the base class, while still being able to easily call a "protected" version of the functions at a later time, you could create a simply wrapper such as the below:
class Base (object):
class Protected (object):
def __init__ (self, target):
self.target = target
def get_message (self):
try:
return self.target.get_message ()
except Exception as detail:
print ("error:", detail)
return None
def __init__ (self):
self.protected = self.Protected (self)
def get_message (self):
raise Exception ("Not Implemented")
class Foo (Base):
def get_message (self):
return "Hello World";
class Bar (Base):
def get_message (self):
raise Exception ("Bar.get_message_impl always fail!")
f = Foo ()
b = Bar ()
f_msg = f.protected.get_message () # protected from failure
b_msg = b.protected.get_message () # protected from failure
b_msg = b.get_message () # will raise exception
NotImplementedError is an exception; don't return it, raise it as an exception:
class Crawler():
def get_image_source_url(self, image_page_soup):
raise NotImplementedError("method get_image_source_url must be implemented")
def get_image_thumbnail_url(self, image_page_soup):
raise NotImplementedError("method get_image_thumbnail_url must be implemented")
def get_tags_container(self, image_page_soup):
raise NotImplementedError("method get_tags_container must be implemented")
You don't need to 'wrap' anything here. If the subclass implements the method, the original method won't be called an no exception is raised.
If further processing is needed, and the subclass implementation is optional but should not be visible to the outside API, you could require subclasses to implement a method by a different name, called from the base class method:
class Crawler():
def _image_source_url_implementation(self, image_page_soup):
raise NotImplementedError("method get_image_source_url must be implemented")
def get_image_source_url(self, image_page_soup):
try:
url = self._image_source_url_implementation(image_page_soup)
except NotImplementedError:
# do something default
url = 'something else'
# process the produced URL further
return processed_result
Here self.get_image_source_url() delegates to an optional self._image_source_url_implementation() method.
Yes, in Python 3.3 SimpleNamespace was added
Unlike object, with SimpleNamespace you can add and remove attributes. If a SimpleNamespace object is initialized with keyword arguments, those are directly added to the underlying namespace.
Example:
import types
x = types.SimpleNamespace()
x.happy = True
print(x.happy) # True
del x.happy
print(x.happy) # AttributeError. object has no attribute 'happy'
You can use type to create a new class on the fly and then instantiate it. Like so:
>>> t = type('test', (object,), {})()
>>> t
<__main__.test at 0xb615930c>
The arguments to type are: Class name, a tuple of base classes, and the object's dictionary. Which can contain functions (the object's methods) or attributes.
You can actually shorten the first line to
>>> t = type('test', (), {})()
>>> t.__class__.__bases__
(object,)
Because by default type creates new style classes that inherit from object.
type is used in Python for metaprogramming.
But if you just want to create an instance of object. Then, just create an instance of it. Like lejlot suggests.
Creating an instance of a new class like this has an important difference that may be useful.
>>> a = object()
>>> a.whoops = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'whoops'
Where as:
>>> b = type('', (), {})()
>>> b.this_works = 'cool'
>>>
It sounds like you want a class factory:
def make_baked_type(fruit, bake):
class baked_item(fruit, bake):
pass
return baked_item
ApplePie = make_baked_type(Apple, Pie)
item = ApplePie()
print(item.flavour())
# or
def make_baked_item(fruit, bake):
return make_baked_type(fruit, bake)()
item = make_baked_item(Apple, Pie)
print(item.flavour())
One possible solution that I don't particularly like is having the individual classes as a properties in a class
class BakedFruitItem:
fruit: Fruit
baked_item: BakedItem
Now that you've explained more fully, it seems that there really is no subclass-specific logic. In that case, inheritance is not the right choice. If there really are only minor ways in which other parts of the code treat products, putting code into product classes would be wrong - a violation of separation of concerns which would encumber the product classes with knowledge of other parts of your system. The sorting example makes that clear. Products should not know how they are sorted or stored or presented; if the code which sorts or presents objects is changed, the product class should not need to be rewritten.
I apologise for doubting you on that. We see so many people here presenting questions which are based on poor OO knowledge and I assumed this was the case when I should have asked for more info first.
In fact...
The method overloading choice, as shown in your example code, is actually going against principles like the separation of concerns and DRY. What is the common thing about the add_product method? It adds a product to the appropriate collection. But in the example code, that single, fundamentally identical task is duplicated in three different places. This is really scarcely different to a chain of if..then..else logic.
- The logic is duplicated and any changes to the common behaviour will have to be duplicated in each place. Mistakes are likely and will go undetected.
- The product-adding code should not need to know what types of products are available. All it needs to know is that there are a variety of products, each of which should be added to an appropriate collection. Making it aware means you have to change this section of the code every time you add a new product.
A better way to do this, if you really do need a separate collection for each type of product, would be to have a map of product collections. The key would be the product type. Then you could have one single product_add method, which consults that map and adds the product to the right place. Even if you discovered that there is some need for different product subclasses, this method doesn't need to know. The object class itself could be the key to the map, for example.
Since more than one method is likely to want to update or read from those collections, it is probably better to have a Products object which stores the objects. Whether it contains a map of N different collections or one collection which it knows how to filter/search by type is an implementation detail which need not concern other parts of your code.
In Summary
Since there really seems to be no product-specific code, extra empty classes are not only useless, they complicated your code and make it more fragile. A 'type' member is better.
- Most of your code doesn't even need to know that there is such a member.
- In each domain (the clearly separate sections of your application), identify those few bits which genuinely need to differentiate between product types.
- Isolate them in special helper classes or functions which will return the appropriate product (or do the appropriate thing which matches the type)
- Maps are very useful (they can contain not only collections but other helper objects/functions to hand back to some other code to do the appropriate thing with this object) but should be hidden from anything but the privileged code which really needs to be product-type aware.
If you do that, not only will your code be cleaner and more elegant (free from ugly if..then..else chains, for one thing), if you ever do find a real need for product subclasses, almost none of your existing code will have to change. And even they need only change their internal implementation, because their code will still be no concern of the product class or any of its subclasses.
There is still plenty of fun to be had with adding new classes in the respective domains to encapsulate any "I know what kind of products there are". One challenge is differentiating between the bits of those code which are domain specific (e.g. sorting in a collection which is only relevant in one domain) from those which are actually general. The list of all valid product types, for example, should be an enum which can be fetched by any product-aware domain-specific code. Any map should use that enum as the type for its key. Any function which takes a product "type" as a parameter should take a parameter only of that enum type and so on. Now, where in your code are you going to define that enum?
I think the reason you are having trouble with which path to take is because there is a fundamental design flaw here. You have anemic classes here, which is itself an OO anti-pattern. The external logic that is switching on the type needs to be brought into the Product family of classes.
The code smell for this is that, no matter which path you go, you will either need to check a type member or instanceof in other places in your code. The other smell is that you will constantly be querying a Product object for data and making decisions based on it (see tell-don't-ask). This means that it will be difficult to modify or add behavior because you will be needing to make changes in many, disparate places rather than in an appropriate class to hold that behavior for each type.
Edit:
The addition of the code details shows there is a little more nuance to your question. Let's start by looking at what we are talking about by "type" of product.
Traditionally, anytime we hear the word "type", we jump to the object concept of Type (which I will capitalize to differentiate). This Type is associated with polymorphic behavior, inheritance, encapsulation, and all the other object-oriented concepts. Here, though, we aren't necessarily describing a Type. We are talking about what looks to be a data attribute called a type. The behavior does not really change based on the type, only the value.
As an example to compare against, we can look at integers. We have ints 1 and 2. The behaviors between them are exactly the same; only the values differ. This means that we shouldn't have an abstract type for int that is implemented as a int1 concrete type and an int2 concrete type, etc. We need one concrete type for all ints, and a simple way to interact between them (like comparison for sorting). Your idea of a product type is the same.
If your products have or will have distinct, polymorphic behaviors, inheritance is a valid solution. Here, though, your type is only a data attribute, like the price and the brand, with no difference in behavior. Therefore, you should have a type attribute like you have a price attribute and a brand attribute.
Ideally, you will want to make your code more open-closed, so there should be a way to do this as elegantly as possible. We can create an enum containing the different types of products that exist, ordered in the order they need to be output (using a static std::set or similar on product might be easier to work with, if you'd rather). The type attribute of the product will point to an enum value. In your product_manager class, instead of three named vectors, you should have a map/dictionary-type structure to map from type enum to the respective vector, generated from the elements in the enum. This way, that structure will automatically add vectors for each type at instantiation. When you add a product, select the vector to add it to based on the type of the product. Then you can iterate over the map, then over each vector to print out the products, grouped by type.
Pseudocode:
enum product_type { electrical, electronic, mechanical }
class product {
//...
get_type() {
return this.type;
}
//...
}
class product_manager {
map<product_type, vector<product>> products;
product_manager() {
foreach(p_type in product_type) {
products.insert(p_type, new vector<product>);
}
}
//...
add_product(product p) {
products.at(p.get_type()).append(p);
}
//...
print_products() {
foreach(p_type in product_type) {
foreach(product in products.at(p_type)) {
print_product(product);
}
}
}
//...
}
If you never want to use the type anywhere else, you can make it private, and make the product_manager a friend to product.
I also want to make a quick point about the example code you posted.
//in real application we don't know instantiation proccess, so don't know what type of product comes to us. test::electrical_product *ep = new test::electrical_product; test::electronic_product *etp = new test::electronic_product; test::mechanical_product *mp = new test::mechanical_product; // set products' members values pm.add_product(ep); pm.add_product(etp); pm.add_product(mp); pm.print_electrical_products(); pm.print_electronic_products(); pm.print_mechanical_products();
You use method overloading with different parameter types to determine which implementation to call. The trouble with this is that, in your real application, all the products would be of type abstract_product *. The compiler routes the method calls based on the compile-time type of the object, meaning any products being added would be sent to a method product_manager.add_product(abstract_product *) instead of the overloaded methods, meaning that it would not do what you intend.