Chapter 8 Composition and Inheritance

8.1 Setup

from pathlib import Path
pkgpath = Path.cwd() / 'composition_inheritance'
sys.path.extend([str(pkgpath)])
from composition_inheritance import market as mk
from composition_inheritance import consumer_noinheritance as cNoInherit
from composition_inheritance import consumer_inheritance as cInherit

In Python, each .py file represents a module. When multiple .py files are inside a folder with a __init__.py file, the folder forms a package.

8.2 Market

Demand = mk.Demand
Supply = mk.Supply
Market = mk.Market
a, b, c, d = 100, 1, -100, 5

8.2.1 Composition (Has-A Relation)

Market has

  • a demand:

\[Q_d(p) = a - b\times p + \epsilon\]

  • a supply

\[Q_s(p) = c + d\times p + u,\] where \(\epsilon\) and \(u\) are aggregate shocks. Different markets have different \((a,b,c,d)\) but faces the same \((\epsilon, u)\).

And has, in theory,

  • an equilibrium condition where demand equals to supply so that

\[ \begin{align} p^* &=[(a-c)+(\epsilon-u)]/(b+d)\\ q^* &= Q_d(p^*) \end{align} \]

Assume a/b (demand y-intercept), -c/d (supply y-intercept) satisfies \(a/b > -c/d\).

(a) p>0, q>0 demandsupplyAxes (c) p>0, q<0 demandsupplyAxes p q (b) p<0, q>0 demandsupplyAxes

Figure 8.1: Composition

In the following example, we set \(a=100, b=1\) and \(c=-100, d=5\)

8.2.2 Demand

Figure 8.2: Demand. (*: instance variable, ** : class variable, +: instance method, ++: class method)

What a demand object (instance) is like:

demand0 = Demand(100, 1)
# keep track of fundamental parameters
demand0.a
demand0.b
# can compute quantity demanded at a price
demand0.Q(15)

# when aggregate shock changes, all demands change at the same time
demand1 = Demand(150, 2)
demand1.Q(15)
Demand.epsilon = 5
demand0.Q(15)
demand1.Q(15)
Demand.epsilon = 0 

Figure 8.4

class Demand:
  # aggregate demand shock
  epsilon = 0
  # for each market's demand:
  def __init__(self, a, b):
    self.a=a
    self.b=b
  def Q(self, p):
    a, b = self.a, self.b
    return a - b*p + self.__class__.epsilon
demand1 = Demand(a, b)
demand1.Q(5)
Demand.epsilon = 10
demand1.Q(5)

8.2.3 Supply

Figure 8.3: Supply. (*: instance variable, ** : class variable, +: instance method, ++: class method)

What a supply object (instance) is like:

supply0 = Supply(100, 1)
# keep track of fundamental parameters
supply0.c
supply0.d
# can compute quantity supplyed at a price
supply0.Q(25)

# when aggregate shock changes, all supplys change at the same time
supply1 = Supply(150, 2)
supply1.Q(25)
Supply.epsilon = 5
supply0.Q(25)
supply1.Q(25)
Supply.epsilon = 0 
class Supply:
  # aggregate supply shock
  u = 0
  # for each market's supply:
  def __init__(self, c, d):
    self.c=c
    self.d=d
  def Q(self, p):
    c, d = self.c, self.d
    return c + d*p + self.__class__.u

8.2.4 Market

Figure 8.4: Market. (*: instance variable, ** : class variable, +: instance method, ++: class method)

class Market: 
  # attributes (properties) for each market:  
  def __init__(self, a, b, c, d):
    # has it's own demand and supply (objects)
    self.demand = Demand(a, b)
    self.supply = Supply(c, d)
  # functions (mechanism) for each market  
  def equilibrium(self):
    a, b, c, d = self.demand.a, self.demand.b, self.supply.c, self.supply.d
    p = ((a-c)+Demand.epsilon-Supply.u)/(b+d)
    q = self.demand.Q(p)
    self.p, self.q = p, q
    return self
market1 = Market(a, b, c, d)
market1.demand.Q(3)
market1.supply.Q(3)
market1.equilibrium().p
market1.equilibrium().q

Note that the instance method .equilibrium has a return of self. This is why we can chain the computation and outcome:

market1.equilibrium().p

8.3 Consumers

8.3.1 Two types

Cobb-Douglas: \[U(x_1, x_2)=x_1^{\alpha}x_2^{1-\alpha}\]

Leontief: \[U(x_1, x_2)=\min(\alpha x_1, (1-\alpha) x_2)\] with \(0<\alpha<1\).

  • Consumer with Cobb-Douglass preference is a consumer.

  • Consumer with Leontief preference is also a consumer.

  • All consumers:

    • have income (heterogeneous)

    • face market prices (homogeneous)

    • face budget constraint

Figure 8.5: Two consumer types. (*: instance variable, ** : class variable, +: instance method, ++: class method)

Consumer_cobbDouglas = cNoInherit.Consumer_cobbDouglas
Consumer_leontief = cNoInherit.Consumer_leontief
class Consumer_cobbDouglas:
  p = [1, 1] # all face market prices, suppose two markets. 
  def __init__(self, income):
    self.alpha = alpha
    self.income = income # all have income
  def bc(self, x): # all face budget constraint
    p1, p2 = self.__class__.p
    x1, x2 = x
    return self.income - p1*x1 - p2*x2
  def utility(self, x):
    x1, x2 = x
    return x1**self.alpha * x2**(1-self.alpha)

class Consumer_leontief:
  p = [1, 1] # all face market prices, suppose two markets. 
  def __init__(self, income):
    self.alpha = alpha
    self.income = income # all have income
  def bc(self, x): # all face budget constraint
    p1, p2 = self.__class__.p
    x1, x2 = x
    return self.income - p1*x1 - p2*x2
  def utility(self, x):
    x1, x2 = x
    return min(self.alpha*x1, (1-self.alpha)*x2)
Consumer_cobbDouglas = cNoInherit.Consumer_cobbDouglas
Consumer_leontief = cNoInherit.Consumer_leontief

8.3.2 Inheritance (Is-A relation)

Note that

  • All consumers:

    • have income (heterogeneous)

    • face market prices (homogeneous)

    • face budget constraint

Figure 8.6: Two consumer types inherit a super class Consumer. (*: instance variable, ** : class variable, +: instance method, ++: class method)

For all consumers

class Consumer:
  p = [1, 1] # all face market prices, suppose two markets. 
  all_consumers = []
  def __init__(self, income):
    self.income = income # all have income
    self.__class__.all_consumers.append(self)
  def bc(self, x): # all face budget constraint
    p1, p2 = self.__class__.p
    x1, x2 = x
    return self.income - p1*x1 - p2*x2
  • Here we embed an instances collector all_consumers (**).

\[U(x1, x2)=x1^{\alpha}*x2^{1-\alpha},\ 0<\alpha<1\]

class Consumer_cobbDouglas(Consumer):
  def __init__(self, alpha, income):
    super().__init__(income)
    self.alpha = alpha
  def utility(self, x):
    x1, x2 = x
    return x1**self.alpha * x2**(1-self.alpha)

super().__init__(...) will make subclass (Consumer_cobbDouglass) inherit the interface income (ie. subclass_instance.income exists) as its instance variable.

Class variables from superclass will be inherited automatically without calling super().

class A:
  c1=1
  def __init__(self, x):
    self.x=x

class B(A):
  def __init__(self, y):
    self.y=y

b = B(3)
b.c1
# Inheritance without inheriting instance variable is not recommended since it ruins the relationship definition between superclass and subclass.

# If b instance does not need x instance variable, then A class should be defined without x instance.
class A:
  c1=1

class B(A):
  def __init__(self, y):
    self.y = y

b = B(3)
b.c1

\[U(x1, x2)=\min(\alpha*x1, (1-\alpha)*x2), \ 0<\alpha<1\]

class Consumer_leontief(Consumer):
  def __init__(self, alpha, income):
    self.alpha = alpha
    super().__init__(income)
  def utility(self, x):
    x1, x2 = x
    return min(self.alpha*x1, (1-self.alpha)*x2)
Consumer = cInherit.Consumer
Consumer_cobbDouglas = cInherit.Consumer_cobbDouglas
Consumer_leontief = cInherit.Consumer_leontief
consumer_cb1 = Consumer_cobbDouglas(0.3, 500)
consumer_lt1 = Consumer_leontief(0.7, 700)

# all consumers
## face market prices
consumer_cb1.p
consumer_lt1.p
## have income
consumer_cb1.income
consumer_lt1.income
## face budget constraint
consumer_cb1.bc([200, 100])
consumer_lt1.bc([200, 500])

## utilities
consumer_cb1.utility([200, 100])
consumer_lt1.utility([200, 500])

## check all consumers
Consumer.all_consumers

Consumer is called a
* super class, parent class, or base class of Consumer_cobbDouglas/Consumer_leontief.

Consumer_cobbDouglas/Consumer_leontief is a
* derived class, child class, or extended class of Consumer. In most applications, only child class are essential objects. Why do we want to build from a parent class instead of the ultimate end class in one stop?

  • easy to maintain.

  • easy to extend.

When an instance has income instance variable, market prices class variable and budget constraint instance method. This instance is a consumer.

When it walks like a duck, quacks like a duck, it must be a duck

8.3.3 Hierachy of inheritance: rationing constraint

There are some consumers living in the area where \(x_2\) are rationed. No one can buy more than \(2\) units of \(x_2\).

Figure 8.7: Inheritance hierarchy. (*: instance variable, ** : class variable, +: instance method, ++: class method)

class ConsumerRation(Consumer):
  def __init(self, income):
    super().__init__(income)
  @staticmethod
  def ration_constraint(x):
    x1, x2 = x
    return 2-x2
class ConsumerRation_cd(ConsumerRation):
  def __init__(self, alpha, income):
    super().__init__(income)
    self.alpha = alpha
  def utility(self, x):
    x1, x2 = x
    return x1**self.alpha * x2**(1-self.alpha)

class ConsumerRation_lt(ConsumerRation):
  def __init__(self, alpha, income):
    super().__init__(income)
    self.alpha = alpha
  def utility(self, x):
    x1, x2 = x
    return min(self.alpha*x1, (1-self.alpha)*x2)
consumerRation_cd1 = ConsumerRation_cd(0.3, 500)
consumerRation_lt1 = ConsumerRation_lt(0.3, 700)
consumerRation_cd1.ration_constraint([2,3])
consumerRation_lt1.ration_constraint([2,3])

8.4 Mixing composition and inheritance

Utility function can be seen as a component of all consumers:

class Utility_cobbDouglas:
  def __init__(self, alpha):
    self.alpha = alpha
  def utility(self, x):
    x1, x2 = x
    alpha = self.alpha
    return x1**alpha*x2**(1-alpha)
class Consumer_cobbDouglas(Consumer):
  def __init__(self, alpha, income):
    super().__init__(income)
    self.alpha = alpha
    self.utilityComponent = Utility_cobbDouglas(self.alpha)
  def utility(self, x):
    return self.utilityComponent.utility(x)

u_cb1 = Utility_cobbDouglas(0.3)
u_cb1.utility([2,3])

cb1 = Consumer_cobbDouglas(0.3, 500)
cb1.utility([2,3])

Why component? \[U(x_1, x_2) = x_1^{\alpha+\eta}x_2^{1-\alpha-\eta}\]

U

8.5 Download

from pathlib import Path
import requests
response = requests.get("https://www.dropbox.com/s/85t567gerqbbso0/composition_inheritance.py?dl=1")
filepath = Path.cwd() / 'composition_inheritance.py'
filepath.write_text(response.text)
pkgpath = Path.cwd() / 'composition_inheritance'
sys.path.extend([str(pkgpath)])
from composition_inheritance import market as mk
from composition_inheritance import consumer_noinheritance as cInherit
from composition_inheritance import consumer_inheritance as cNoInherit

8.6 Exercise

1. Social welfare

class Equilibrium:
  def __init__(self, market):
    self.market = market
  def market_clear(self):
    a, b, c, d = self.market.demand.a, self.market.demand.b, self.market.supply.c, self.market.supply.d
    p = ((a-c)+Demand.epsilon-Supply.u)/(b+d)
    q = self.market.demand.Q(p)
    self.p, self.q = p, q
    return self
market1.equilibrium().socialwelfare()

8.6.1 2. Constraint

Budget constraint and rationing constraint can also be component that derived from a class call Constraint. Define Constraint class and modify your consumer classes accordingly.

knitr::opts_chunk$set(message=F, eval=F)
klippy::klippy(lang=c("r","python"))