Attribute name change for inherited classes. Possible/Bad practice? - python

Q1. If I have a very general class, with an attribute whose name could be better represented in more specific inherited classes, how can I access the same methods from the parent class if the attribute has changed its name? For example (not my real scenario, but it shows what I mean).
class Entity(object):
def __init__(self):
self.members= {}
... # Methods that use self.members
class School(Entity):
def __init__(self):
super(Entity,self).__init__(self)
class Company(Entity):
def __init__(self):
super(Entity,self).__init__(self)
for class School and for class Company, I would like to be able to use attributes that are more specific, such as self.students and self.employees, but that still work with the methods that were defined for self.members in the class Entity.
Q2. Would this be bad practice? What would be the best way to approach this? In my real case, the word I used for self.members is too general.

Renaming an attribute in a subclass is bad practice in general.
The reason is that inheritance is about substitutability. What it means for a School to be an Entity is that you can use a School in any code that was written to expect an Entity and it will work properly.
For example, typical code using an Entity might do something like this:
for member in entity.members:
If you have something that claims to be an Entity (and even passes isinstance(myschool, Entity)), but it either doesn't have members, or has an empty members, because its actual members are stored in some other attribute, then that code is broken.
More generally, if you change the interface (the set of public methods and attributes) between a base class and dericed class, the derived class isn't a subtype, which means it usually shouldn't be using inheritance in the first case.1
If you make students into an alias for members, so the same attribute can be accessed under either name, then you do have a subtype: a School has its students as members, and therefore it can be sensibly used with code that expects an Entity:
myschool.students.append(Person(cap'))
# ...
for member in myschool.members:
# now cap is going to show up here
And this works just as well with methods defined in Entity:
def slap_everyone(self):
for member in self.members:
# this will include cap
member.slap()
myschool.slap_everyone()
And you can do this by using #property.
class Student(Entity):
# ...
#property
def students(self):
return members
#students.setter
def students(self, val):
self.members = val
#students.deleter
def students(self):
del self.members
So, this isn't flat-out invalid or anything.
But it is potentially misleading.
Will it be obvious to readers of your code that adding cap to myschool.students is going to add him to myschool.members? If so, it's probably OK. If not, or if you're not sure, then you probably shouldn't do this.
Another thing to consider is that a School might have multiple kinds of members: students, teachers, administrators, dropouts who hang around their old campus because they don't know where else to find drug dealers, … If that's part of your design, then what you really want is for members to be a property, and probably a read-only property at that,2 and each subclass can define what counts as "members" in a way that makes sense for that subclass.
class Entity(object):
#property
def members(self):
return []
def rollcall(self):
return ', '.join(self.members)
class School(Entity):
def __init__(self):
super(School, self).__init__()
self.students, self.teachers = [], []
#property
def members(self):
return self.students + self.teachers
school = School()
school.teachers.append('cap')
school.students.extend(['marvel', 'america, planet'])
print(school.rollcall())
This will print out:
cap, marvel, america, planet
That school is working as a School, and as an Entity, and everything is good.
1. I say usually because (regardless of what OO dogma says) there are other reasons for subclassing besides subtyping. But it's still the main reason. And in this case, there doesn't appear to be any other reason for subclassing—you're not trying to share storage details, or provide overriding hooks, or anything like that.
2. In fact, you might even want to drag in the abc module and make it an abstract property… but I won't show that here.

Related

Multiple classes object "chaining"?

I am new to Python and I have prior experience in VBA.
I wish to create Classes in Python such that I could achieve the following syntax:
Company('MS').Department('Finance').Employee('John').FullName = 'John MacDonalds'
Company('MS').Department('Finance').Employee('John').Wages = '5000'
I am beginning to realize that declaring the Class structure in Python is very different from VBA.
Am I on the right approach? If not, will there be any other recommended approach for this Parent/Child structure?
I really don't think that's a good use of inheritance. Good inheritance is when the child class can easily be substituted for the parent class with no difficulties. Is an employee substitutable for a department? That doesn't seem right. Wouldn't a department be able to do tons of things an employee couldn't? For every function you have for a department that an employee can't do, you would have to have a "throw new not implemented" in the child class.
This is known as the Liskov Substitution Principle. Basically subclasses must be substitutable for their base classes. My favorite example is with birds and penguins. You could make a bird class and have lots of different kinds of birds inherit from this class. But what happens when you create a penguin class inheriting from bird? Penguins can't fly but birds can. So in the penguin class, you would implement this function and would have to say "throw new not implemented." Its not good because if you put a penguin in where a function is expecting a bird, it will freak out and crash when you give it a penguin.
I hope that helps. I'm not too familiar with Python specifically but I do understand inheritance.
You haven't really said what the syntax you want is supposed to accomplish which would be useful to know...
Nesting classes as shown below would allow you to achieve the syntax you want, but the result is not only very "unpythonic" — it's also completely useless and I can't think a reasonable way to implement to change that.
Without more information about what you're doing, at the moment I can think of an alternative to suggest. I suggest you just create a bunch of regular non-nested classes and explicitly instantiate them when needed.
Note that I've changed the names you were to follow the PEP 8 naming conventions.
class Company:
def __init__(self, name):
self.name = name
class Department:
def __init__(self, dept_name):
self.dept_name = dept_name
class Employee:
def __init__(self, given_name):
self.given_name = given_name
Company('MS').Department('Finance').Employee('John').full_name = 'John MacDonalds'
Here's a way to implement your tree structure via Python "autovivification" using the Vividict dictionary defined in this answer.
I've adapted it to support different Vividict subclasses at each level in the hierarchy: i.e. Company, Department, and Employee. Each subclass defines a class atribute named subtype the the Vividict base class' __missing__() will use when creating missing key values. I also added __repr__() method to each one to make instance print out all the items they contain.
This is just a proof-of-concept and I really don't know if you can use it or not, but it should give you a good idea of one way of doing what you want with some fairly readable syntax.
class Vividict(dict): # Base class.
subtype = dict
def __missing__(self, key):
value = self[key] = self.subtype(key)
return value
class Employee: # Leaf - Regular class
def __init__(self, given_name):
self.given_name = given_name
def __repr__(self):
return f'{self.__class__.__name__}({self.given_name!r})'
class Department(Vividict): # Branch.
subtype = Employee
def __init__(self, dept_name):
self.dept_name = dept_name
def __repr__(self):
return (f'{self.__class__.__name__}({self.dept_name!r})' + '\n '
+ repr(list(self.values())))
class Company(Vividict): # Root of tree.
subtype = Department
def __init__(self, co_name):
self.co_name = co_name
def __repr__(self):
return (f'{self.__class__.__name__}({self.co_name!r})' + '\n '
+ repr(list(self.values())))
company = Company('MS')
company['Finance']['George'].full_name = 'George Brown'
company['Finance']['Mary'].full_name = 'Mary Jones'
print(company)
Output:
Company('MS')
[Department('Finance')
[Employee('George'), Employee('Mary')]]

Design pattern to allow attaching one class to another

I am designing an RPG and would like to have the ability to attach classes to each other. What I'm looking to do is have say an Item class. The weapon class would inherit from it. A sword would be an instance of the weapon class. I want to then be able to attach properties to the sword. These properties would be other classes. For example I could attach the container class to it and the sword (only that instance of the sword) would become a container. I could also maybe attach something like an enchantment to that sword.
For a bonus it would be nice to be able to combine instances as well. So instead of having to have a fire_enchantment class I could just make it an instance of Enchantment and attach it to the sword instance.
I've googled around and haven't been able to find a design pattern that fits this. I recall studying one but can't remember what it was called (Was a few years ago)
I'm at a loss of of which design pattern allowed this. The combining of multiple classes dynamically.
I think you seem to understand the idea of inheritance in python (e.g. class Subclass(Superclass): ) so I won't cover that here.
The classes you want to 'attach' can be treated as any other variable within the Weapon class.
class Enchantment(object):
def __init__(self, name, type):
self.name = name
self.type = type
# can define more member variables here, and set with setter methods
# more Enchantment methods here...
class Weapon(object):
def __init__(self, name, type)
self.name = name
self.type = type
self.enchantments = []
# more Weapon member variables here
def add_enchantment(self, enchantment):
# any logic you need to check when adding an enchantment
self.enchantments.append(enchantment)
Then in wherever your game code is running you could do
sword = Weapon('My sword', 'sword')
fire_enchantment = Enchantment('Fireball', 'fire')
sword.add_enchantment(fire_enchantment)
You can then add methods on the Weapon class to do things with the enchantments/add certain logic.
The enchantment is still an instance of an object, so if you access it in the list (maybe by identifying it by its name, or looping through the list) all its methods and variables are accessible. You just need to build an interface to it via the Weapon class e.g. get_enchantment(self, name), or have other methods in the Weapon class interact with it (e.g. when you attack you might loop through the enchantments and see if they add any extra damage).
There's obviously design considerations about how you design your classes (the above was thrown together for example and doesn't include inheritance). For example you might only allow one enchantment per weapon, in which case you shouldn't use a list in the weapon object, but could just set self.enchantment = None in the constructor, and set self.enchantment = enchantment in the add_enchantment method.
The point I'm making is you can treat instances of Enchantment or other 'attachable' classes as variables. Just make sure you create an instance of the class e.g. fire_enchantment = Enchantment('Fireball', 'fire').
There's plenty of reading out there in terms of inheritance and OOP in general. Hope this helps!
Additional answer from OP
I think the Mixin pattern is what I was looking for. After digging around more I found this post which has an answer for dynamic mixin's.

How do you keep code consistent with multiple developers?

I know that Python is a dynamically typed language, and that I am likely trying to recreate Java behavior here. However, I have a team of people working on this code base, and my goal with the code is to ensure that they are doing things in a consistent manner. Let me give an example:
class Company:
def __init__(self, j):
self.locations = []
When they instantiate a Company object, an empty list that holds locations is created. Now, with Python anything can be added to the list. However, I would like for this list to only contain Location objects:
class Location:
def __init__(self, j):
self.address = None
self.city = None
self.state = None
self.zip = None
I'm doing this with classes so that the code is self documenting. In other words, "location has only these attributes". My goal is that they do this:
c = Company()
l = Location()
l.city = "New York"
c.locations.append(l)
Unfortunately, nothing is stopping them from simply doing c.locations.append("foo"), and nothing indicates to them that c.locations should be a list of Location objects.
What is the Pythonic way to enforce consistency when working with a team of developers?
An OOP solution is to make sure the users of your class' API do not have to interact directly with your instance attributes.
Methods
One approach is to implement methods which encapsulate the logic of adding a location.
Example
class Company:
def __init__(self, j):
self.locations = []
def add_location(self, location):
if isinstance(location, Location):
self.locations.append(location)
else:
raise TypeError("argument 'location' should be a Location object")
Properties
Another OOP concept you can use is a property. Properties are a simple way to define getter and setters for your instance attributes.
Example
Suppose we want to enforce a certain format for a Location.zip attribute
class Location:
def __init__(self):
self._zip = None
#property
def zip(self):
return self._zip
#zip.setter
def zip(self, value):
if some_condition_on_value:
self._zip = value
else:
raise ValueError('Incorrect format')
#zip.deleter
def zip(self):
self._zip = None
Notice that the attribute Location()._zip is still accessible and writable. While the underscore denotes what should be a private attribute, nothing is really private in Python.
Final word
Due to Python's high introspection capabilities, nothing will ever be totally safe. You will have to sit down with your team and discuss the tools and practice you want to adopt.
Nothing is really private in python. No class or class instance can
keep you away from all what's inside (this makes introspection
possible and powerful). Python trusts you. It says "hey, if you want
to go poking around in dark places, I'm gonna trust that you've got a
good reason and you're not making trouble."
After all, we're all consenting adults here.
--- Karl Fast
You could also define a new class ListOfLocations that make the safety checks. Something like this
class ListOfLocations(list):
def append(self,l):
if not isinstance(l, Location): raise TypeError("Location required here")
else: super().append(l)

What is this python concept called? Parent class using child-only attributes.

I am writing some code that is an upside down triangle of inheritance. I have a base Linux class that has a CLIENT attr which holds a connection. I have several APIs that are logically separated (kvm, yum, gdb, dhcp, etc..) that use CLIENT but I only want the user to need to create a single instance of Linux class but be able to call all the methods from the Parent classes. While maintaining the nice logical code separation among the parents:
class Linux(
SSHClient,
yum.Yum,
kvm.Kvm,
ldap.Ldap,
lshw.Lshw,
packet_capture.Tcpdump,
tc.TrafficControl,
networking.Networking,
gdb.Gdb,
dhcp.Dhcp,
httputil.Http,
scp.Scp,
fileutils.FileUtils):
I made a little example:
class Dad(object):
def __init__(self):
raise NotImplementedError("Create Baby instead")
def dadCallBaby(self):
print('sup {}'.format(self.babyName))
class Mom(object):
def __init__(self):
raise NotImplementedError("Create Baby instead")
def momCallBaby(self):
print('goochi goo {}'.format(self.babyName))
class Baby(Mom, Dad):
def __init__(self, name):
self.babyName = name
def greeting(self):
self.momCallBaby()
self.dadCallBaby()
x=Baby('Joe')
x.greeting()
What is doing this called? Is this Duck Typing? And is there a better option?
There's really no such thing as "child-only attributes".
The attribute babyName is just stored in each object's namespace, and looked up there. Python doesn't care that it happened to be stored by Baby.__init__. And in fact, you can write store the same attribute on a Mom that isn't a Baby and it will work the same way:
class NotABaby(Mom):
def __init__(self): pass
mom = NotABaby()
mom.babyName = 'Me?'
mom.momCallBaby()
Also, it's hard to suggest a better way to do what you're doing, because what you're doing is inherently confusing and probably shouldn't be done.
Inheritance normally means subtyping—that is, Baby should only be a subclass of Mom if every Baby instance is usable as a Mom.1
But a baby is not a mom and a dad.2 A baby has a mom and a dad. And the way to represent that is by giving Baby attributes for its mom and dad:
class Baby(object):
def __init__(self, mom, dad, name):
self.mom, self.dad, self.name = mom, dad, name
def greeting(self):
self.mom.momCallBaby(self.name)
self.dad.dadCallBaby(self.name)
Notice that, e.g., this means that the same woman can be the mom of two babies. Since that's also true of the real-life thing you're modeling here, that's a sign that you're modeling things correctly.
Your "real" example is a little less clear, but I suspect the same thing is going on there.
The only reason you want to use inheritance, as far as I can tell, is:
I only want the user to need to create a single instance of Linux class
You don't need, or want, inheritance for that:
class Linux(object):
def __init__(self):
self.ssh_client = SSHClient()
self.yum = yum.Yum()
# etc.
… but be able to call all the methods from the Parent classes
If yum.Yum, ldap.Ldap and dhcp.Dhcp both have methods named lookup, which one would be called by Linux.lookup?
What you probably want is to just leave the attributes as public attributes, and use them explicitly:
system = Linux()
print(system.yum.lookup(package))
print(system.ldap.lookup(name))
print(system.dhcp.lookup(reservation))
Or you'll want to provide a "Linux API" that wraps all the underlying APIs:
def lookup_package(self, package):
return self.yum.lookup(package)
def lookup_ldap_name(self, name):
return self.ldap.lookup(name)
def lookup_reservation(self, reservation):
return self.dhcp.lookup(reservation)
If you really do want to just forward every method of all of your different components, and you're sure that none of them conflict with each other, and there are way too many to write out manually, you can always do it programmatically, by iterating all of the classes, iterating inspect.getmembers of each one, filtering out the ones that start with _ or aren't unbound methods, creating a proxy function, and setattr-ing it onto Linux.
Or, alternatively (probably not as good an idea in this case, but very commonly useful in cases that aren't that different), you can proxy dynamically, at method lookup time, by implementing a __getattr__ method (and, often, a __dir__ method).
I think one of these two kinds of proxying may be what you're really after here.
1. There are some cases where you want to inherit for reasons other than subtyping. For example, you inherit a mixin class to get implementations for a bunch of methods. The question of whether your class is usable wherever that mixin's instances are usable doesn't really make sense, because the mixin isn't usable anywhere (except as a base class). But the subtyping is still the standard that you're bending there.
2. If it is, call Child Protective Services. And also call Professor X, because that shouldn't be physically possible.

Subclass avoiding parent's metaclass

Say I have a third-party library where a metaclass requires me to implement something. But I want to have an intermediate "abstract" subclass that doesn't. How can I do this?
Consider this to be a very minimal example of what third-party library has:
class ServingMeta(type):
def __new__(cls, name, bases, classdict):
if any(isinstance(b, ServingMeta) for b in bases):
if "name" not in classdict:
# Actual code fails for a different reason,
# but the logic is the same.
raise TypeError(f"Class '{name}' has no 'name' attribute")
return super().__new__(cls, name, bases, classdict)
class Serving(object, metaclass=ServingMeta):
def shout_name(self):
return self.name.upper()
I cannot modify the code above. It's an external dependency (and I don't want to fork it).
The code is meant to be used this way:
class Spam(Serving):
name = "SPAM"
spam = Spam()
print(spam.shout_name())
However, I happen to have a lot of spam, and I want to introduce a base class with the common helper methods. Something like this:
class Spam(Serving):
def thrice(self):
return " ".join([self.shout_name()] * 3)
class LovelySpam(Spam):
name = "lovely spam"
class WonderfulSpam(Spam):
name = "wonderful spam"
Obviously, this doesn't work and fails with the well-expected TypeError: Class 'SpamBase' has no 'name' attribute declared. Would third-party library had a SpamBase class without a metaclass, I could've subclassed that - but no such luck this time (I've mentioned this inconvenience to the library authors).
I can make it a mixin:
class SpamMixin(object):
def thrice(self):
return " ".join([self.shout_name()] * 3)
class LovelySpam(SpamMixin, Serving):
name = "lovely spam"
class WonderfulSpam(SpamMixin, Serving):
name = "wonderful spam"
However, this makes me and my IDE cringe a little, as it quickly becomes cumbersome to repeat SpamMixin everywhere and also because object has no shout_name attribute (and I don't want to silence analysis tools). In short, I just don't like this approach.
What else can I do?
Is there a way to get a metaclass-less version of Serving? I think of something like this:
ServingBase = remove_metaclass(Serving)
class Spam(ServingBase, metaclass=ServingMeta):
...
But don't know how to actually implement remove_metaclass and whenever it's even reasonably possible (of course, it must be doable, with some introspection, but it could require more arcane magic than I can cast).
Any other suggestions are also welcomed. Basically, I want to have my code DRY (one base class to rule them all), and have my linter/code analysis icons all green.
The mixin approach is the correct way to go. If your IDE "cringe" that is a deffect on that tool - just disable a little of the "features" that are obviously incorrect tunning when coding for a dynamic language like Python.
And this is not even about creating things dynamically, it is merely multiple-inheritance, which is supported by the language since forever. And one of the main uses of multiple-inheritance is exactly being able to create mixins just as this one you need.
Another inheritance-based workaround is to make your hierarchy one level deeper, and just introduce the metaclass after you come up with your mixin methods:
class Mixin(object):
def mimixin(self): ...
class SpamBase(Mixin, metaclass=ServingMeta):
name = "stub"
Or just addd the mixin in an intermediate subclass:
class Base(metaclass=Serving Meta):
name = "stub"
class MixedBase(Mixin, Base):
name = "stub"
class MyLovingSpam(MixedBase):
name = "MyLovingSpam"
If you don't want to be repeating the mixin=-base name in every class, that is the way to go.
"Removing" a metaclass just for the sake of having a late mixin is way over the top. Really. Broken. The way to do it wol e re-create the class dynamically, just as #vaultah mentions in the other answer, but doing that in an intermediate class is a thing you should not do. Doing that to please the IDE is something you should not do twice: messing with metaclasses is hard enough already. Removing things on inheritance/class creation that the language puts there naturally is something nasty (cf. this answer: How to make a class attribute exclusive to the super class ) . On the other hand, mixins and multiple inheritance are just natural.
Are you still there? I told you not to do so:
Now, onto your question - instead of "supressing the metaclass" in an intermediate class, it would be more feasible to inherit the metaclass you have there and change its behavior - so that it does not check for the constraints in specially marked classes - create an attribute for your use, like _skip_checking
class MyMeta(ServingMeta):
def __new__(metacls, name, bases, namespace):
if namespace.get("_skip_checking", False):
# hardcode call to "type" metaclass:
del namespace["_skip_checking"]
cls = type.__new__(metacls, name, bases, namespace)
else:
cls = super().__new__(metacls, name, bases, namespace)
return cls
# repeat for __init__ if needed.
class Base(metaclass=MyMeta):
_skip_checking = True
# define mixin methods
class LoveSpam(Base):
name = "LoveSpam"
There's really no direct way to remove the metaclass from a Python class, because the metaclass created that class. What you can try is re-create the class using a different metaclass, which doesn't have unwanted behaviour. For example, you could use type (the default metaclass).
In [6]: class Serving(metaclass=ServingMeta):
...: def shout_name(self):
...: return self.name.upper()
...:
In [7]: ServingBase = type('ServingBase', Serving.__bases__, dict(vars(Serving)))
Basically this takes the __bases__ tuple and the namespace of the Serving class, and uses them to create a new class ServingBase. N.B. this means that ServingBase will receive all bases and methods/attributes from Serving, some of which may have been added by ServingMeta.

Categories

Resources