Nested classes for cleaner inheritance? [closed] - python

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Say I have a class BigObject, which contains within it a number of SmallObjects - and this is the only place where SmallObject is used.
class SmallObject:
pass
class BigObject:
def __init__(self):
self.objects = [SmallObject() for _ in range(10)]
This is all fine, until I want a second version of these two, which inherits some behaviour and overrides some; inheritance seems natural, except:
class NewSmallObject(SmallObject):
pass
class NewBigObject(BigObject):
def __init__(self):
super().__init__()
self.objects = [NewSmallObject for _ in range(10)]
We had to create a bunch of SmallObjects only to immediately override them with NewSmallObjects. Which is not great if e.g. SmallObjects are expensive to create. Also, if we change how the list of SmallObjects is created in BigObject, those changes don't get passed on to NewBigObject.
The solution I came up with was to use nested classes:
class BigObject:
class SmallObject:
pass
def __init__(self):
self.objects = [self.SmallObject() for _ in range(10)]
class NewBigObject(BigObject):
class SmallObject(BigObject.SmallObject):
pass
This deals with both the issues described above. My main concern is that when I looked on StackOverflow for questions about nested classes in Python people keep saying nested classes are unpythonic, and I'd like to understand why. It can also create quite deeply nested classes if SmallObject contains TinyObjects which contain MinisculeObjects etc, which may be the answer?
So my question is basically:
is this a "good" solution to this problem?
if not, what would a good alternative be?

The solution is, as you've already found, to make SmallObject an attribute of the BigObject class.
There is nothing inherently wrong with using a nested class for this, but the readability of your code may suffer if the nested class is very long. Generally speaking, I would recommend to define SmallObject in the global scope though. After all, the Zen of Python says "Flat is better than nested". If you keep nesting TinyObjects and MinisculeObjects, your code will quickly become unreadable:
class BigObject:
class SmallObject:
class TinyObject:
class MinisculeObject:
... # MinisculeObject class body
... # TinyObject class body
... # SmallObject class body
... # BigObject class body
Defining your classes in the global scope only requires minimal extra effort, and looks much cleaner:
class MinisculeObject:
... # MinisculeObject class body
class TinyObject:
miniscule_object_factory = MinisculeObject
... # TinyObject class body
class SmallObject:
tiny_object_factory = TinyObject
... # SmallObject class body
class BigObject:
small_object_factory = SmallObject
... # BigObject class body

This looks like a good use case for the Abstract Factory pattern.
The gist is that there is a class hierarchy for creating things that derive from SmallObject. That way subclasses of BigObject will all use the same interface to get their SmallObject instances.

Related

Partially initializing object attributes using functools.partial in python [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 months ago.
Improve this question
I'm wondering whether it is a valid use case of functools.partial to partially initialize object attributes in python. Let's say I have some base class A that has three object-level attributes (attr1, attr2, attr3):
class A:
def __init__(self, attr1, attr2, attr3):
self.attr1 = attr1
self.attr2 = attr2
self.attr3 = attr3
def do_something(self):
pass
Now I want to create objects based on this base class for which one of the attributes (say attr1) is always fixed, but the other two may vary. One way I can think of doing this is to define a class that inherits from base class A and has a fixed class-level attribute which is used during initialization, something like this:
class PartialA(A):
attr1="foo" # fixed class-level attribute
def __init__(self, attr2, attr3):
super().__init__(self.attr1, attr2, attr3)
I can then create objects by specifying only the varying object-level attributes, like so:
partial_a = PartialA(attr2="baz", attr3="baz")
Another way to achieve a similar behavior would be to use functools.partial to partially initialize the __init__ of the base class A and then create objects off of it:
from functools import partial
PartialA = partial(A, attr1="foo")
partial_a = PartialA(attr2="bar", attr3="baz")
Are both approaches equally valid, or are there dangers/drawbacks to the approach using functools.partial in this way that I am currently not aware of? Would be happy to hear your opinions!
I tried both of the approaches outlined above and so far, I can not really see any difference in terms of behavior. But there might well be some reasons to favor one over another that I'm currently unaware of.
In both cases, you are basically defining factory functions to create instances of A. I would take a third route, an alternate constructor implemented using a class method.
class A:
def __init__(self, attr1, attr2, attr3):
self.attr1 = attr1
self.attr2 = attr2
self.attr3 = attr3
#classmethod
def with_foo(cls, attr2, attr3):
return cls("foo", attr2, attr3)
def do_something(self):
pass
a = A.with_foo("bar", "baz") # a = A("foo", "bar", "baz")
One major difference between this and your two attempts is that you can't override how A.with_foo sets the first attribute. (With the class, you can simply redefine PartialA.attr1 before creating an instance, plus you now have a weird second class that doesn't need to exist. With partial, you can still pass your own value of attr1 as a keyword argument.)

Explanation of 'list' and 'object' as parameter in class definition [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Can somebody please explain what is the purpose of 'object' and 'list' parameters in the classes Card and StandardDeck? I find little information about this.
Phycharm says this about 'object' in class Card():
The base class of the class hierarchy.
When called, it accepts no arguments and returns a new featureless instance that has no instance attributes and cannot be given any.
Is class card considered base class because of the 'object' parameter? Does the super().__init__() from class StandardDeck inherited from class Card? I really hope someone can give a good explanation i have been struggling for hours.
def main():
class Card(object):
def __init__(self, value, suit):
self.value = value
self.suit = suit
class StandardDeck(list):
def __init__(self):
super().__init__()
suits = list(range(4))
values = list(range(13))
[[self.append(Card(i, j)) for j in suits] for i in values]
deck = StandardDeck()
for card in deck:
print(card)
main()
object is the base class (also referred to a super class) for the derived class Card. This means Card inherit all the functionality and state of the base class (and as others said already this is implied anyhow), and it allows Card to override (or change) methods as needed. Another way to say that is Card is a more specialized class than object.
Similarly, list is the base class for StandardDeck.
I would also add that it's not a particular good design. For instance, list has a method called clear(). What does it mean to clear() a StandardDeck? It would be better design to use whatever data structures are needed an implementation detail (instance variables). This is sometimes expressed as failing the Liskov substitution principle (LSP).

Which of these is the best practice for accessing a variable in a class? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
If I have an object, and within that object I've defined a variable, which of these methods would be considered 'best' for accessing the variable?
Method One
Using a getter function
class MyClass:
def __init__(self):
self.the_variable = 21 * 2
def get_the_variable(self):
return self.the_variable
if __name__ == "__main__"
a = MyClass()
print(a.get_the_variable())
Method Two
Using the #property decorator
class MyClass:
def __init__(self):
self._the_variable = 21 * 2
#property
def the_variable(self):
return self._the_variable
if __name__ == "__main__"
a = MyClass()
print(a.the_variable)
Method Three
Simply accessing it directly
class MyClass:
def __init__(self):
self.the_variable = 21 * 2
if __name__ == "__main__"
a = MyClass()
print(a.the_variable)
Are any of these methods more pythonic than the others?
Method 3 is the standard pythonic way to start. If you need additional logic, filtering or some other behavior for the attribute you can always go back and add a method for the attribute and use the #property decorator at a later time. That's the beauty of python, start with something simple that works. If you later need finer control over the attribute you can create the property and not have to update/change any of the client code that uses the attribute. The client code will not know the difference between accessing the attribute directly vs calling a method and as a result does not have to change.
This ideology is confirmed via PEP 549
Python's descriptor protocol guides programmers towards elegant API design. If your class supports a data-like member, and you might someday need to run code when changing the member's value, you're encouraged to simply declare it as a simple data member of the class for now. If in the future you do need to run code, you can change it to a "property", and happily the API doesn't change.
I think it's not easy to answer since it's based on the program.
class MyClass:
def __init__(self):
self.the_variable = 21 * 2
def get_the_variable(self):
return self.the_variable
But if you want to pass a class attirubete to some variable, I think it's better to use getter-setter, since it is more readable and understandable. Because you are basically telling I ask this value. For example:
if __name__ == "__main__":
a = MyClass()
modified_variable = a.get_the_variable() * 2
In contrary, if you are just using that class attribute, third option a.the_variable is better.
if a.get_the_variable() == 42:
# do something
else:
# do something

Python add object to list inside same object method [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I need to add an object, e.g. Person, to a list that is given as a parameter. This is an example of the code
class Person:
def __init__(self, age):
self.age = age
def add_to_list(self, list_of_persons):
list_of_persons.append(self)
I know this situation may be caused by bad code design, however, it is working on a small test that I implemented. My question is, will this design cause issues in future? If yes, which ones?
I don't like your solution for various reasons, which include:
Elements should not be responsible for their membership in a container. It should be the container responsibility to add and remove then.
You generally don't expect methods to only modify the parameter. Methods are generally used to modify the instance, possibly, with some side-effects on the parameters, but I wouldn't expect a method to only change the parameter.
The method doesn't add any real functionality except that it hides how Person is inserted into a list. If you follow this design why didn't you, for example, add a method to remove a Person from the list?
I find:
person.add_to_list(people)
Quite unreadable.
If you think you aren't going to change the container for the Person, i.e. you will always use a list instead, why don't you simply use a list directly? It's simpler, has less overhead and makes you write less code:
people = []
people.append(Person(18))
If you think that you are probably going to change the container used, then I believe it's better to write a People class with an add and a remove method (and whatever else you need). Behind the scenes People can use whatever container it wants to implement these methods. In this way you get a readable, intuitive and robust design:
class People(object):
def __init__(self, people=()):
self._members = list(people)
# add other methods/special methods like __len__ etc.
def add(self, person):
self._members.append(person)
def remove(self, person):
self._members.remove(person)
people = People()
people.add(john)
people.add(jack)
# Change list to set?
class People(object):
def __init__(self, people=()):
self._members = set(people)
def add(self, person):
self._members.add(person)
def remove(self, person):
self._members.remove(person)
I can't really think of a situation where you couldn't replace a call to your person.add_to_list(list) with a list.append(person) call.
The problem here is that you are adding a dependency between your class Person and the list object that hurts encapsulation. If later you want to replace the list with another data structure that doesn't feature the "append" method (such as a custom container), you'll have to add another method Person.add_to_container to retain consistency, or add a append method to your other data structure (if you can and if this makes sense).

How do I implement this python old style class as a new style class [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
I am trying to implement this in the new style class and failing. In this example I want to create forests with different populations of plants and animals.
class forest:
def __init__(self,p,a):
self.alive="alive"
self.pl = []
self.ani = []
for i in range(p):
self.pl.append(self.Plant())
for i in range(a):
self.ani.append(self.Animal())
class Plant:
def __init__(self):
self.synth = "co2"
self.name = "coconut"
class Animal:
def __init__(self):
self.carb="o2"
self.name="orangutan"
Edit:
As Lazlow requested.
My problem was that I was confused between nesting and inheritance.
so in my approaches I was trying to do this
class forest(object):
....
....
class Plant(forest):
instead of using class Plant(object). I was also trying to define functions which should have been in forest class in Plant class. I was trying to use /super/ keyword to achieve things which I should no have. I was then lost in reading about creating descriptors to do what I wanted. I assumed that all of these attempts would distract from the problem I had. They were instantly cleared after I read the first response.
My thanks to both of you.
Regards,
abcd
The difference between old-style class declarations and new-style ones is that new-style classes must inherit from object (or some other new-style class). So, to make your classes new-style, just put (object) after the class name:
class forest(object):
# ...
You're also nesting classes, which isn't forbidden in either new or old-style classes, but which is often confusing and unnecessary. Just unindent your Plant and Animal class declarations and you should be fine. You can make them new-style too, if you want, by inheriting from object.
Note that if you're using Python 3, all classes are new-style classes. You don't need to explicitly inherit from object, as the compiler will add that for you if you omit it. It's often a good idea to make it explicit, especially if you use both Python 2 and Python 3 at different times.

Categories

Resources