Classes - "delegation" question.

dieter dieter at handshake.de
Thu Dec 18 02:24:12 EST 2014


"Ivan Evstegneev" <webmailgroups at gmail.com> writes:
> I have a question about "delegation" coding pattern(I'm working with Python
> 3.4).

Unlike Java, Python supports "multiple inheritance". This means
that you need "delegation" much more rarely in Python.
Python does not have much special support for delegation: usually,
you must delegate explicitely - maybe using a delagating decorator.

> ...
> When code looks like in  Case 1, it actually works with standard
> "inheritance" model. (with sub and super-class definitions).
> Also according to the book:
> ***************************************************************
> This code leverages the fact that a class's method can always be called
> either through
> an instance (the usual way, where Python sends the instance to the self
> argument
> automatically) or through the class (the less common scheme, where you must
> pass the
> instance manually). In more symbolic terms, recall that a normal method call
> of this
> form:
> 	instance.method(args...)
> is automatically translated by Python into this equivalent form:
> 	class.method(instance, args...)
>  ***************************************************************
>
> Furthermore:
>
> ***************************************************************
> Python provides it automatically only for calls made through an instance.
> For calls 
> through the class name, you need to send an instance to self yourself; for
> code inside 
> a method like giveRaise, self already is the subject of the call, and hence
> the instance 
> to pass along.
> Calling through the class directly effectively subverts inheritance and
> kicks the call
> higher up the class tree to run a specific version.
> ****************************************************************
>
> What is considered as a "call" in this context?

Something of the form "<expression>(args...)". I.e. any call of
a function or method.

The discussion above does not directly apply to your situation.
It targets more specifically the following case:

   class Base:
     ...
     def f(self, ...):
       ...

   class Derived(Base):
     ...
     def f(self, ...):
       ...
       Base.f(self, ...)
       ...

i.e., you have two classes "Base", and "Derived" where
"Derived" overrides the inherited "f" and its definition
involves the use of the inherited "f". Inside the redefinition,
you must make explicit that you mean the inherited "f" and not
the "f" being defined. In those cases, you typically use
the pattern "Base.f", meaning the "f" defined in "Base".
However, you may use the form anywhere - its just rarer in other
cases.

It you use "instance.f", then "instance" is automatically
passed as first argument to "f"; however, it you use
"class.f", then nothing is passed on automatically as first
parameter. When "class.f" is finally called, you usually need
to pass an instance of "class" as first parameter explicitely.


> Is it a code inside class method?

Usually, you use "class.f" inside the redefinition of "f"
in a derived class. But, you can use it anywhere (if you like).

> For instance(relates to the "Case 1"):
>
> def giveRaise(self, percent, bonus = .10):
> 		Person.giveRaise(self, percent + bonus)
>
> The "call" will be : Person.giveRaise(self, percent + bonus)   ?

Yes.

> In such a case, when I create an instance:
>
>>>> tom = Manager('Tom Jones', 50000)
>
> and then invoke giveRaise method 
>
>>>> tom.giveRaise(.10)
>
> I essentially say this:
>
> "class.method(instance, args...) "
>
> Where class is "Person" and method is "giveRaise" and instance is "tom"?

Yes.

> Moreover, because of how it defined (a method giveRaise), I make a "call"
> through the class name?

Loosely speaking, yes.

> What is the "call" through instance? 

Your "tom.giveRaise". There "tom" is a "Manager" instance.
>
> Is it this one(according to Case 1):
>
> class Person:
> 	.......
> 	def giveRaise(self, percent):
> 		self.pay = int(sel.pay * (1 + percent))
> 	.......
>
> and then:
>
>>>> bob = Person('Bob Smith', 'dev', 30000)
>>>> bob.giveRaise(.10)     <   ---- Is this call through instance? 

Yes.


> ...
> Questions regard to "Case 2" example:
>
> class Person: 
> 	....the same...
>
> class Manager: 
> 	def __init__(self, name, pay):
> 		self.person = Person(name, 'mgr', pay)   <-------
> Initialization of Manager's attribute named  'self.person" and embedding  a
> Person object. 
> 	
> 	def giveRaise(self, percent, bonus = .10):                < ------
> Defenition of Manager's method named "giveRaise" and according to the book:
> 		self.person.giveRaise(percent + bonus)	 "Intercept and
> delegate"
> 	..............
>
> My question here is substantially the same:
>
> On the one hand we embedding "Person" into "Manager" class, 
>
> On the other hand, when I write this code:
>
>>>> tom = Manager('Tom Jones', 5000)
>
> I create an instance of Manager that have an attribute named: tom.person,
> which in turn is a class named Person.

        ... is an instance of a class ...


> And when typing:
>
>>>>tom.giveRaise(.10)
>
> I do kinda same thing. 

Indeed -- just a bit more convoluted in the case that
the "derived" class would not need to override an "inherited" method.

> For my assumption I say:
>
> Person.giveRaise( percent + bonus)         (when self.person actually
> implies "Person")
>
> But why we don't use "self" argument in --> self.person.giveRaise(percent +
> bonus)?

Because "self.person" is an instance of "Person", not the
class "Person". You have here an "instance method call" (which
automatically passes the instance as first argument).




More information about the Python-list mailing list