I am investigating the source code of a package, and noticed that classes are able to call certain methods that aren't defined within the class.
For example:
inst = ClassA()
meth = inst.meth1()
"some stuff printing to console"
However, meth1() is not defined within ClassA. In the ClassA definition, there is an input that references another class:
from package.sub.file import ClassB
class ClassA(ClassB):
...normal class stuff...
From another file:
class ClassB:
...normal class stuff...
def meth1(self):
...stuff...
My main question is how is this possible? How does meth1 become a method for ClassA? I am confused as to why passing ClassB as an input transfers all the methods associated with ClassB to ClassA
This is inheritance, a common concept in object-oriented programming.
When one class (the child) inherits from another (the parent), an instance of the child is treated exactly the same as an instance of the parent. That means that if a parent defines a method, it is available to instance of its child as well.
As to how Python implements inheritance, buckle up :)
When ClassA is created, it has an attribute called the method-resolution order (MRO) added to it.
>>> ClassA.__mro__
(<class '__main__.ClassA'>, <class '__main__.ClassB'>, <class 'object'>)
This is built using ClassA, its parent classes, and their parent classes, all the way up to object, the ultimate base class; and it is used for all sorts of attribute lookups.
A somewhat abridged account:
When you try to call inst.meth1, Python goes through the following steps (some steps omitted for clarity and brevity):
Does inst.meth1 exist? No. Start checking the classes in the MRO
Does ClassA.meth1 exist? No, check the next class.
Does ClassB.meth1 exist? Yes.
What is ClassB.meth1? It's a function; call it with inst as the first argument.
Thus (without going into the descriptor protocol in detail), inst.meth1() is equivalent to ClassB.meth1(inst).
Related
I checked the SocketServer.py package.
Find the one function of class BaseServer--- '_handle_request_noblock()'
It has :request, client_address = self.get_request()
But the function get_request() is built from BaseServer's subclass TCPServer and UDPServer.
How can I understand this?
On top of Dynamic Dispatch as referred to by #JeremyFisher, such behavior is also related to the OOP concepts of Inheritance and Polymorphism (or this) which in turn is related to Liskov's Substitution Principle. In Python, this behavior is visible in MRO.
Let's say we have this hierarchy of classes
>>> class BaseServer:
... def handle_request_noblock(self):
... print("BaseServer:handle_request_noblock")
... self.get_request()
...
>>> class TCPServer(BaseServer):
... def get_request(self):
... print("TCPServer:get_request")
...
>>>
>>> class UDPServer(BaseServer):
... def get_request(self):
... print("UDPServer:get_request")
...
>>>
Let's see the attributes of the child class objects
>>> # base_server = BaseServer() # This should not be done because "abstract" base classes are just blueprints, thus they are incomplete as in our example here where it has no definition of <get_request>. A derived "concrete" subclass is needed.
>>>
>>> tcp_server = TCPServer()
>>> udp_server = UDPServer()
>>>
>>> print(dir(tcp_server))
[...<trimmed for better viewing>..., 'get_request', 'handle_request_noblock']
>>> print(dir(udp_server))
[...<trimmed for better viewing>..., 'get_request', 'handle_request_noblock']
>>>
As you can see, both subclasses has the handle_request_noblock attribute even if they don't explicitly define it, they just inherited it from the base class.
an object created through inheritance, a "child object", acquires all the properties and behaviors of the "parent object"
Then, they have the get_request which they implemented on their own.
Let's call now the entrypoint function
>>> tcp_server.handle_request_noblock()
BaseServer:handle_request_noblock
TCPServer:get_request
>>> udp_server.handle_request_noblock()
BaseServer:handle_request_noblock
UDPServer:get_request
>>>
As you can see, BaseServer:handle_request_noblock was able to call e.g. TCPServer:get_request. Why? Remember that the object we used is a TCPServer instance and based on the available methods we displayed on the earlier step, we saw that both handle_request_noblock and get_request are available to it, so we know that both can be called. Same with UDPServer.
In Python, the technicalities of such dispatch from base to derived class is through MRO.
Python supports classes inheriting from other classes. The class being inherited is called the Parent or Superclass, while the class that inherits is called the Child or Subclass. In python, method resolution order defines the order in which the base classes are searched when executing a method. First, the method or attribute is searched within a class and then it follows the order we specified while inheriting.
>>> print(TCPServer.mro())
[<class '__main__.TCPServer'>, <class '__main__.BaseServer'>, <class 'object'>]
>>> print(UDPServer.mro())
[<class '__main__.UDPServer'>, <class '__main__.BaseServer'>, <class 'object'>]
>>>
So for TCPServer
This means that when we called handle_request_noblock, it checked first if the implementation is present in class TCPServer, since it isn't, it checks to the next which is class BaseServer, since it is there, then it uses that implementation, so if you override it within TCPServer, it wouldn't have to call the implementation in the BaseServer anymore.
Then when we called get_request, it checked first if the implementation is present in class TCPServer, since it is there, then it uses that implementation, no more need to check if it is present in BaseServer.
Same idea with UDPServer.
I have a class with a private constant _BAR = object().
In a child class, outside of a method (no access to self), I want to refer to _BAR.
Here is a contrived example:
class Foo:
_BAR = object()
def __init__(self, bar: object = _BAR):
...
class DFoo(Foo):
"""Child class where I want to access private class variable from parent."""
def __init__(self, baz: object = super()._BAR):
super().__init__(baz)
Unfortunately, this doesn't work. One gets an error: RuntimeError: super(): no arguments
Is there a way to use super outside of a method to get a parent class attribute?
The workaround is to use Foo._BAR, I am wondering though if one can use super to solve this problem.
Inside of DFoo, you cannot refer to Foo._BAR without referring to Foo. Python variables are searched in the local, enclosing, global and built-in scopes (and in this order, it is the so called LEGB rule) and _BAR is not present in any of them.
Let's ignore an explicit Foo._BAR.
Further, it gets inherited: DFoo._BAR will be looked up first in DFoo, and when not found, in Foo.
What other means are there to get the Foo reference? Foo is a base class of DFoo. Can we use this relationship? Yes and no. Yes at execution time and no at definition time.
The problem is when the DFoo is being defined, it does not exist yet. We have no start point to start following the inheritance chain. This rules out an indirect reference (DFoo -> Foo) in a def method(self, ....): line and in a class attribute _DBAR = _BAR.
It is possible to work around this limitation using a class decorator. Define the class and then modify it:
def deco(cls):
cls._BAR = cls.__mro__[1]._BAR * 2 # __mro__[0] is the class itself
return cls
class Foo:
_BAR = 10
#deco
class DFoo(Foo):
pass
print(Foo._BAR, DFoo._BAR) # 10 20
Similar effect can be achieved with a metaclass.
The last option to get a reference to Foo is at execution time. We have the object self, its type is DFoo, and its parent type is Foo and there exists the _BAR. The well known super() is a shortcut to get the parent.
I have assumed only one base class for simplicity. If there were several base classes, super() returns only one of them. The example class decorator does the same. To understand how several bases are sorted to a sequence, see how the MRO works (Method Resolution Order).
My final thought is that I could not think up a use-case where such access as in the question would be required.
Short answer: you can't !
I'm not going into much details about super class itself here. (I've written a pure Python implementation in this gist if you like to read.)
But now let's see how we can call super:
1- Without arguments:
From PEP 3135:
This PEP proposes syntactic sugar for use of the super type to
automatically construct instances of the super type binding to the
class that a method was defined in, and the instance (or class object
for classmethods) that the method is currently acting upon.
The new syntax:
super()
is equivalent to:
super(__class__, <firstarg>)
...and <firstarg> is the first parameter of the method
So this is not an option because you don't have access to the "instance".
(Body of the function/methods is not executed unless it gets called, so no problem if DFoo doesn't exist yet inside the method definition)
2- super(type, instance)
From documentation:
The zero argument form only works inside a class definition, as the
compiler fills in the necessary details to correctly retrieve the
class being defined, as well as accessing the current instance for
ordinary methods.
What were those necessary details mentioned above? A "type" and A "instance":
We can't pass neither "instance" nor "type" which is DFoo here. The first one is because it's not inside the method so we don't have access to instance(self). Second one is DFoo itself. By the time the body of the DFoo class is being executed there is no reference to DFoo, it doesn't exist yet. The body of the class is executed inside a namespace which is a dictionary. After that a new instance of type type which is here named DFoo is created using that populated dictionary and added to the global namespaces. That's what class keyword roughly does in its simple form.
3- super(type, type):
If the second argument is a type, issubclass(type2, type) must be
true
Same reason mentioned in above about accessing the DFoo.
4- super(type):
If the second argument is omitted, the super object returned is
unbound.
If you have an unbound super object you can't do lookup(unless for the super object's attributes itself). Remember super() object is a descriptor. You can turn an unbound object to a bound object by calling __get__ and passing the instance:
class A:
a = 1
class B(A):
pass
class C(B):
sup = super(B)
try:
sup.a
except AttributeError as e:
print(e) # 'super' object has no attribute 'a'
obj = C()
print(obj.sup.a) # 1
obj.sup automatically calls the __get__.
And again same reason about accessing DFoo type mentioned above, nothing changed. Just added for records. These are the ways how we can call super.
In Python 2.7.10
class OneMixin(object):
def __init__(self):
# super(OneMixin, self).__init__()
print "one mixin"
class TwoMixin(object):
def __init__(self):
# super(TwoMixin, self).__init__()
print "two mixin"
class Account(OneMixin, TwoMixin):
def __init__(self):
super(Account, self).__init__()
print "account"
The Account.mro() is: [<class 'Account'>, <class 'OneMixin'>, <class 'TwoMixin'>, <type 'object'>]
Although every class is listed in the MRO, "two mixin" is not printed.
If I uncomment the super calls in OneMixin and TwoMixin, the MRO is exactly the same, but the "two mixin" IS printed.
Why the difference? I would expect every thing in the MRO to be called.
This is because super is used to delegate the calls to either parent or sibling class of a type. Python documentation has following description of the second use case:
The second use case is to support cooperative multiple inheritance in a dynamic execution environment. This use case is unique to Python and is not found in statically compiled languages or languages that only support single inheritance. This makes it possible to implement “diamond diagrams” where multiple base classes implement the same method. Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).
If you remove the super call from OneMixin there's nothing delegating the call to the next type in MRO.
The reason is that you're overriding the __init__ method of the parent class. The method resolution order will be the same, regardless of what is in your __init__ method.
The way super works is that it will pass it down to the next class down the line in the method resolution order. By commenting out that line in OneMixin, you break the chain. super is designed for cooperative inheritance.
Also, __init__ is not truly a class constructor, either. This may trip you up if you think of it as you would a constructor in other languages.
I want to use the superclass to call the parent method of a class while using a different class.
Class AI():
...
for i in self.initial_computer_group:
if i.rect.x == current_coords[0] and i.rect. y== current_coords[1]:
i.move(coords_to_move[0], coords_to_move[1])
i.move() calls a method from an inherited class, when I want the original method from the parent class.
self.initial_computer_group contains a list of objects which are completely unrelated to the AI class.
I know I need to somehow get the class name of the current object i references to, but then I don't know what to use as the second argument in super() as i can't use self, since it's unrelated to AI.
So how do I use super() when I'm in a completely different class to what super is meant to call?
Note: I want to call the parent method as it speeds everything up. I only designed the inherited method to ensure the human isn't breaking the rules in this chess game.
EDIT: I found a solution by changing the name of the inherited method to something else, but I was wondering whether there's still a special way to invoke super() to solve the problem
It sounds like you want to call a specific class's method, no matter what the inheritance graph looks like (and in particular, even if that method happens to be overridden twice). In that case, you don't want super. Instead, call the class's method directly. For example, assuming the version you want is in the Foo class:
Foo.move(i, coords_to_move[0], coords_to_move[1])
As it's hard to read code in comments, here's a simple example:
class BaseClass():
def func(self):
print("Here in BaseClass.")
class InheritedClass(BaseClass):
def func(self):
print("Here in InheritedClass.")
def func(instance):
super(InheritedClass, instance).func()
In use:
>>> func(InheritedClass())
Here in BaseClass.
But this clearly makes your code less flexible (as the instance argument must be an InheritedClass instance), and should generally be avoided.
Given some inheritance hierarchy:
class Super: # descends from object
def func():
return 'Super calling'
class Base(Super):
def func():
return 'Base calling'
class Sub(Base):
def func():
return 'Sub calling'
You can get the resolution hierarchy with the __mro__ attribute:
>>> s=Sub()
>>> s.__class__.__mro__
(<class '__main__.Sub'>, <class '__main__.Base'>, <class '__main__.Super'>, <class 'object'>)
Then you can pick among those by index:
>>> s.__class__.__mro__[-2]
<class '__main__.Super'>
>>> s.__class__.__mro__[-2].func()
Super calling
You can get a specific name by matching against the __name__ attribute:
def by_name(inst, tgt):
for i, c in enumerate(inst.__class__.__mro__):
if c.__name__==tgt:
return i
return -1
Then if you want to call the parent class of an unrelated class, just use one of these methods on an instance of the descendant class with the method of interest.
Of course the simplest answer is if you know the class and method you want, just call it directly:
>>> Super.func()
Super calling
>>> Base.func()
Base calling
If you need to go several levels up (or an unknown number of levels up) to find the method, Python will do that for you:
class Super:
def func():
return 'Super calling'
class Base(Super):
pass
class Sub(Base):
pass
>>> Sub.func()
Super calling
I'm currently implementing some unit tests for my company's build scripts. To eliminate bloat and make it a little easier to implement new tests, I'm making all my test classes inherit from a custom subclass called BasicTest that inherits from PyUnit's TestCase.
There are currently two functions that all tests utilize from BasicTest: The constructor (Although it could obviously be overwritten in the future) and the runTest() method that is the default method name that the super's constructor uses if no value is passed in (e.g. BasicTest() would create a test that will execute the runTest() method when called upon, whereas BasicTest('Foo') would use the Foo() method).
I would like to make runTest() simply run all possible tests from the inheriting object it is called on. However, as runTest() is defined only in BasicTest and inherited by the subclasses, I'm looking for a way to dynamically call all of the subclass' methods from the super. I know this violates the rules of OO programming, but from what I can see, Python was never one to follow rules in the first place :)
For clarity, the following illustrates my intentions:
I want runTest() to be called from a subclass object and only handle that object's methods. Let's say SubclassTest() that has methods TestParse() and TestExec(). I want it so that:
sub = SubClassTest()
sub.runTest()
runs TestParse() and TestExec(), but I want the runTest() method to be defined in and inherited from BasicTest without being overriden.
one can create metaclass which will collect all interesting methods of subclasses into class property
class TestMetaclass(type):
def __new__(cls, name, bases, attrs):
own_tests = [v for k,v in attrs.iteritems() if k.startswith('test')]
attrs['test_method_list'] = own_tests
return super(TestMetaclass, cls).__new__(cls, name, bases, attrs)
set this metaclass to base class as __metaclass__
and implement runTests method
class BaseTest():
test_method_list = []
__metaclass__ = TestMetaclass
def runTests(self):
for method in self.test_method_list:
method(self)
And after this all subclasses will be constructed using this metaclass
class TestOne(BaseTest):
def test_foo(self):
pass
In the end one can use collected methods running runTests() method
TestOne().runTests()
Sample code:
load base class .py file as module
and inspect
import inspect
import imp
imp.load_source((name of class by which to want that module), (path base class name of file).py)
module = __import__((name of class by which to want that module))
inspect.getmembers(module) will give you dict of name, cls
Hope this helps