【CS 61a study notes 5】 Class&Object

Class

A class combines( and abstracts) data and functions.
A object is an instantiation of class.
You can think a class as a blue print for a house ,so you just know the structure of this house and how to build it ,you can not really live in it . And the object is the house itself.

Class Statement

class < name >:
< suite >
Assignment & def statement in < suite > create attributes of the class (not names in frames).

Object Construction

When a class is called:

  • A new instance of that class is created;
  • The _init_ method of the class is called with the new object as its first argument (named self), along with any additional arguments provided in the call expression.
    • _init_ is called a constructor.
    • We can think self argument as the object we create . That is , self is the name that is going to refer to an instance of the class.
    • The _init_ method should return None, or it will occur the TypeError. Even if we put or call a function with the return value in the _init_ method, when we call _init_() ,it will return None .

Object Identity:

  • Every object that is an instance of a user-defined class has a unique identity
  • We often use “is” and “is not” to test if two expression evaluate to the same project
  • Binding an object to a new name using assignment does not create a new object .
    • if we assign c = a , then we test " c is a" ,we will get True.

Method

Method are defined in the suite of a class statement. We use def statement to create a method. These def statements create function objects as always , but their names are bound as attributes of the class.

Invoking method & Dot expressions

All invoked methods has access to the object via self parameter , and so they can all access and manipulate the object’s state.

# simple class 
class Account:
	interest = 0.02 # a class attribute
	# self is always a name for an object which is an instance of this account class
	def __init__(self,account_holder):
		self.name = account_holder # instance attribute
		self.balance = 0 # instance attribute
	
	def deposit(self,amount):
		self.balance = self.balance + amount 
		return self.balance
	
	def withdraw(self,amount):
		if amount > self.balance:
			return 'Insufficient Funds'
		else:
			self.balance = self.balance - amount
			return self.balance

tom_account = Account('Tom')
getattr(tom_account,'balance')
# 0
hasattr(tom_account,'deposit')
# True
tom_account.interest 
# 0.02

Dot Notation :

  • Dot notation automatically supplies the first argument to a method.
    • type(Account.deposit) is a class function with two arguments
    • type(tom_account.deposit) is a class method with one argument
    • Object + function = Method
    • self is always a name for an object which is an instance of this account class
    • Every time we use self within one of these methods or attributes, we are referring to a particular account
    • Every time we are referring to self.something and assigning to it, it is a form of mutation. Actually what we’re changing is self , is the account object . So you may see many methods in classes do not have the return statement .When we use such method without return , we just wanna to change some values . When the method is called , the self.something in that method will be changed automatically in that class.
  • Objects receive message via dot notation.
  • Dot notation access attributes of the instance or its class.
    < expression > . < name >
    • Evaluates to the value of the attribute looked up by < name > in the object that is the value of the < expression >
    • Expression can be any valid python expression

Accessing attributes

Using getattr , we can look up an attribute using a string . getattr and dot expressions look up a name in the same way.
Looking up an attribute name in an object may return : one of its instance attributes or one of the attributes of its class.
the order of finding an attribute/ a value : instance --> class --> base class

Looking up Attributes by name

__< expression > . < name > __
To evaluate a dot expression :

  1. Evaluate the < expression > to the left of the dot, which yields the object of the dot expression
  2. < name > is matched against the instance attributes of that object ; if an attribute with that name exists ,its value is returned
  3. if not ,< name > is looked up in that class ,which yields a class attribute value
  4. That value is returned unless it is a function ,in which case a bound method is returned instead

Methods and Functions

A method is an attribute.
Bound Methods ,which couple together a function and the object on which that method will be invoked.
Object + Function = Bound Method
The object always is passed in the first argument of the method , that is, the self argument.

Class attributes

Class attributes are “shared” across all instances of a class because they are attributes of the class ,not the instance .
While in the above example interest is an attribute of class Account and if we call tom_account.interest we will get 0.02 , the interest is not part of the instance that was somehow copied from the class. If we override the interest value in the tom instance ,it won’t change the interest value in the class.

All the attributes in the _init_ method are instance attribute

create class attribute:

  1. build it in the class < suite > outside the all the method of the class
  2. using assignment outside the class . For example , just assign Account.interest = 0.03 .If the class already has the interest attribute , this assignment will change the value of attribute interest .If the class Account do not have such attribute ,it will create new one.

Attribute Assignment

Assignment statements with a dot expression on their left-hand side affect attributes for the object of that dot expression:

  • If the object is an instance ,then assignment sets an instance attribute
  • If the object is a class ,then assignment sets a class attribute.
  • <object>.<attribute> = <expression> , attribute assignment statement adds or modifies the attribute of the object.

Inheritance

Inheritance is a method for relating classes together .
A common use: two similar classes differ in their degree of specialization.
The specialized class may have the same attributes as the general class, along with som special-case behavior.
class <name> (< base class >):
< suite>

The subclass may override certain inherited attributes.Using inheritance ,we implement a subclass by specifying its differences from the base class.

Looking up Attribute Names on Classes

Base class attribute are not copied into subclass.

To look up a name in a class :

  1. If it names an attribute in the class ,return the attribute value
  2. Otherwise , look up the name in the base class ,if there is one
# example 
class A:
	z = -1 
	def f(self,x):
		return B(x-1)

class B(A):
	n = 4
	def __init__(self,y):
		if y:
			self.z = self.f(y)
		else:
			self.z = C(y+1)

class C(B):
	def f(self,x):
		return x

# we assign :
a = A()  # empty instance 
b = B(1) # assign 1 to the y of __init__() in B
b.n = 5	# only change the n-value of b instance 

# we need to figure out 
print(C(2).n)
print(C(2).z)
print(C.z == a.z)
print(B.z)
print(b.z)
print(b.z.z)
print(b.z.z.z)
print(b.z.z.z.z) 

Firstly ,we can write down all the things in the class A B C:

  • class A

    • z : -1
    • f : -----> func f(self, x)
  • class B inherits from A

    • n = 4
    • _init_ : -----> func _init_(self, y)
  • class C inherits from B

    • f : -----> func f(self, x)
      Secondly , we need to figure out the assignments:
  • a = A()

    • a is an instance of A
    • Since there is no _init_ method within the class A definition, there is no instance attributes that are set .
    • This a instance is just a blank slate
  • b = B(1) & b.n = 5

    • calling B() introduces a new instance of class B ,and we assign is to b in the global frame.
    • the _init_ method for B is called automatically with self bound of the instance b and y bound to the 1
    • Now z is set ,although it seems a list bit complicate. z is in the b instance
    • Then the attribute assignment b.n = 5 means that we add an attribute to the instance b, which has no effect on B class or its base class instead it’s just a change to this particular instance b.

Then, the questions:

  • C(2).n = ?

    1. class C does not have _init_ method , so we find in its base class B, and there is one then the 2 will be passed to the y-value of the _init_ method in class B.
    2. Now we know that y is assigned to 2, so the statement if y is True, then self.z = self.f(2). At this time self is the instance of class C, so we find func-f in class C , and finally we can know that self.z = 2 . Then we are going to add z attribute to the C instance .So C(2).z = 2
    3. After we create an instance C(2), we can see that there is no n in the instance and class C , so we lookup in its base class B, the we find n = 4 , so C(2).n = 4
  • C.z ?= a.z

    1. It is simple to find a.z. First we look up in the instance a , there is no z.Then we find it in the class A , and there exists z = -1 ,so a.z=-1
    2. Then we need to find C.z .First we’d like to find it in the class C , but there is no z in it . Then we find it in it base class B , no z in B. Finally we find it in class A , because A is the base class of B, then we find there exists z = -1 ,so C.z=-1
    • Notice that C is not an instance , it is just the class C.
  • b.z

    1. b = B(1) , b has an instance attribute called z .When b instance was created we passed in 1 as the value for y, self is bound to the instance itself . Then we checked that if y is True , so we got self.z = self.f(1).
    2. Since Neither instance b or class B has method f, so we find f in the base class A .
    3. Then we passed in 1 as the value of x , self is bound to the instance b itself, we got the return B(0) which creates a new instance of B . so b.z is the instance B(0).
    4. Now we have a new instance B(0) ,which will automatically invoke the _init_ method and pass the 0 to y. Then we check if y is False ,now self.z = C(1) . The self is the instance B(0) , and self.z will create a new instance C(1) . b.z.z is the instance C(1)
    5. Since neither class C or instance C(1) has the _init_ method , so we look up in the base class B . Then we bind self to C(1) , 1 is passed to y . Checking that if y is True , we can get self.z = f(1) .Notice that now the self is Instance C(1) ,so we will first check whether instance C(1) has the function f . Then we pass in 1 as the value of x , and get the return value 1 . so at this time , in the instance C(1) , self.z = 1, which means b.z.z.z = 1
    6. If we continue to figure out b.z.z.z.z , we will get the error .Since int type does not have the attribute of z.

Designing for Inheritance

  • Do not repeat your self; Using existing Implementations ;
  • Attributes that have been overridden are still accessible via class objects
  • Look up attribute on instances whenever possible
# inheritance example for above class Account
class CheckingAccount(Account):
	""" A bank account that charges for withdrawals"""
	withdraw_fee = 1 # class attribute
	interest = 0.01 # class attribute
	def withdraw(self,amount):
		return Account.withdraw(self,amount + self.withdraw_fee) 

# Account.withdraw() is the attribute look-up on the base class (class Account)
# we can see we only change the amount value from amount to amount + self.withdraw_fee which allows for specialized account 

Inheritance and Composition

OOP shines when we adopt the metaphor.

  • Inheritance is best for representing is-a relationships.
  • E.g., a checking account is a specific type of account
  • so, CheckingAccount inherits from Account
  • Composition is best for representing has a relationships
  • E.g., a bank has a collection of bank accounts it manages
  • So ,A bank has a list of accounts as an attribute
# example for Composition
class Bank:
	""" A bank *has* accounts
	>>> bank = Bank()
	>>> john = bank.open_account('John',10)
	>>> jack = bank.open_account('Jack',5,CheckingAccount)
	>>> john.interest 
	0.02
	>>> Jack.interest
	0.01
	>>> bank.pay_interest()
	>>> john.balance
	10.2
	"""
	def __init__(self):
		self.accounts = []
	
	def open_account(self, holder, amount, kind=Account):
		account = kind(holder)
		account.deposit(amount)
		self.accounts.append(account)
		return account
	
	def pay_interest(self):
		for a in self.accounts:
			# a is an Account/CheckingAccount instance
			a.deposit(a.balance*a.interest)
			
	def too_big_to_fail(self):
		return len(self.accounts)>1

Multiple Inheritance

A class may inherit from multiple base class in Python. But it will make program complicated

# example 
# single inheritance 
class SavingsAccount(Account):
	deposit_fee = 2
	def deposit(self,amount):
		return Account.deposit(self,amount-self.deposit_fee)

# Multiple Inheritance
class AsSeenOnTVAccount(CheckingAccount, SavingsAccount):
	"""
	if CheckingAccount and SavingsAccount have the same attribute or method ,first check CheckingAccount .
	
	>>> such_a_deal = AsSeenOnTVAccount("John")
	>>> such_a_deal.balance # instance attribute
	1
	>>> such_a_deal.deposit(20) # SavingsAccount Method
	19
	>>> such_a_deal.withdraw(5) # CheckingAccount Method
	13
	"""
	def __init(self,account_holder):
		self.holder = account_holder
		self.balance = 1 # a free dollar
AsSeenOnTVAccount
CheckingAccount
SavingsAccount
Account

Using super()

We do not need to pass self when we use super() notation.
super() is not referring to the Account class it’s like referring to a version of self as if Account where it is class instead of CheckingAccount where it’s class.

class CheckingAccount(Account):
	""" A bank account that charges for withdrawals"""
	withdraw_fee = 1 # class attribute
	interest = 0.01 # class attribute
	def withdraw(self,amount):
		# return Account.withdraw(self,amount + self.withdraw_fee) 
		# alternatively :
		return super().withdraw(amount + self.withdraw_fee) 

Hw04

class VendingMachine

class VendingMachine:
    """A vending machine that vends some product for some price.
    
    Creat a class call VendingMachine that represents a vending machine for some product.A VendingMachine object returns strings describing its iteractions.
    Fill in the VendingMachine class add attribute and methods asa appropriate, such that its behavior matched the following doctests
    
    some useful string syntax
    >>> ten,twenty,thirty = 10 ,'twenty',[30]
    >>> '{0} plus {1} is {2}'.format(ten,twenty,thirty)
    '10 plus twenty is [30]'

    >>> v = VendingMachine('candy', 10)
    >>> v.vend()
    'Inventory empty. Restocking required.'
    >>> v.add_funds(15)
    'Inventory empty. Restocking required. Here is your $15.'
    >>> v.restock(2)
    'Current candy stock: 2'
    >>> v.vend()
    'You must add $10 more funds.'
    >>> v.add_funds(7)
    'Current balance: $7'
    >>> v.vend()
    'You must add $3 more funds.'
    >>> v.add_funds(5)
    'Current balance: $12'
    >>> v.vend()
    'Here is your candy and $2 change.'
    >>> v.add_funds(10)
    'Current balance: $10'
    >>> v.vend()
    'Here is your candy.'
    >>> v.add_funds(15)
    'Inventory empty. Restocking required. Here is your $15.'

    >>> w = VendingMachine('soda', 2)
    >>> w.restock(3)
    'Current soda stock: 3'
    >>> w.restock(3)
    'Current soda stock: 6'
    >>> w.add_funds(2)
    'Current balance: $2'
    >>> w.vend()
    'Here is your soda.'
    """
    "*** YOUR CODE HERE ***"
    
    def __init__(self,name,price,stock = 0,money = 0):
        self.name = name 
        self.price = price
        self.stock = stock
        self.money = money
        
    
    def vend(self):
        if not self.stock:
            return 'Inventory empty. Restocking required.'
        elif self.money < self.price:
            gap = self.price - self.money
            return 'You must add ${} more funds.'.format(gap)
        elif self.money >= self.price:
            change = self.money - self.price
            self.money = 0
            self.stock -=1
            if change:
                return 'Here is your {0} and ${1} change.'.format(self.name,change)
            else:
                return 'Here is your {}.'.format(self.name)
         
    
    def add_funds(self,add_money):
        if not self.stock:
            return 'Inventory empty. Restocking required. Here is your ${}.'.format(add_money)
        else:
            self.money = self.money + add_money
            return 'Current balance: ${}'.format(self.money)


    def restock(self,add_stock):
        self.stock = self.stock + add_stock
        return 'Current {0} stock: {1}'.format(self.name,self.stock)
        

class Mint


class Mint:
    """A mint creates coins by stamping on years.

    The update method sets the mint's stamp to Mint.current_year.
    
    complete the Mint and Coin classes so that the coins created by a mint have the correct year and worth.
        * each Mint Instance has a year stamp,The update method sets the year stamp to the current_year class attribute of the Mint class
        * The create method takes a subclass of Coin and returns an instance of that class stamped with the mint's year(which may be different from Mint.current_year if it has not been updated)
        * A Coin's worth method returns the cents value of the coin plus one extra cent for each year of age beyond 50. A coin's  age can be determined by substracting the coin's year from the current_year class attribute of the Mint class.

    >>> mint = Mint()
    >>> mint.year
    2020
    >>> dime = mint.create(Dime)
    >>> dime.year
    2020
    >>> Mint.current_year = 2100  # Time passes
    >>> nickel = mint.create(Nickel)
    >>> nickel.year     # The mint has not updated its stamp yet
    2020
    >>> nickel.worth()  # 5 cents + (80 - 50 years)
    35
    >>> mint.update()   # The mint's year is updated to 2100
    >>> Mint.current_year = 2175     # More time passes
    >>> mint.create(Dime).worth()    # 10 cents + (75 - 50 years)
    35
    >>> Mint().create(Dime).worth()  # A new mint has the current year
    10
    >>> dime.worth()     # 10 cents + (155 - 50 years)
    115
    >>> Dime.cents = 20  # Upgrade all dimes!
    >>> dime.worth()     # 20 cents + (155 - 50 years)
    125
    """
    current_year = 2020

    def __init__(self):
        self.update()

    def create(self, kind):
        "*** YOUR CODE HERE ***"
        # kind is a subclass of coin , Nickel and Dime do not have __init__ so they will inherit the class Coin's method
        return kind(self.year)
        

    def update(self):
        "*** YOUR CODE HERE ***"
        self.year = Mint.current_year
     

class Coin:
    def __init__(self, year):
        self.year = year

    def worth(self):
        "*** YOUR CODE HERE ***"
        if self.year< Mint.current_year
        	return self.cents + Mint.current_year - self.year - 50  
        else:
        	return self.cents
        

class Nickel(Coin):
    cents = 5

class Dime(Coin):
    cents = 10

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值