"Ivan Evstegneev" <webmailgro...@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). -- https://mail.python.org/mailman/listinfo/python-list