unittest로 안전한 코드 관리를~

|

Unittest

Python에서는 unittest 모듈이 존재한다. 이를 사용하면 단위 테스트를 쉽게 할 수 있다.

unittest의 모듈에는 다음 녀석들이 있다.

  • TestCase Class : 실제 test code를 넣을 녀석
  • TestSuite Class : testcase들을 묶어서 돌릴 녀석

그림으로 표현하면 다음과 같다. 1.png

먼저 TestCase 작성법과 TestCase만으로 돌리는 방법을 살펴보자.


TestCase

unittest.TestCase를 상속받아서 새로운 testcase를 만든다.

  • setUp : 테스트 코드 시작시 공통적으로 넣을 코드를 넣는다.
  • test~ : setUp이후 테스트할 코드. 복수의 함수를 만든다.
  • tearDown : test~가 끝난 후 공통으로 처리해야할 코드를 넣는다.

예제를 보자.

# coding: utf-8
import unittest
 
def sum(a,b):
    return a+b
 
class ModuleTest1(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def testsum1(self):
        self.assertEqual(sum(1,2),3)
        
    def testsum2(self):
        self.assertEqual(sum(1,-1),0)
 
if __name__ == '__main__':
    unittest.main()

####실행결과

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
 
OK

자주쓰는 함수

테스트 할 때 자주 쓰는 녀석들. 실제 사용해보니 Equal은 결과값이 다른 경우 쉘에 결과값을 보여주기 때문에 더 유용하다.

  • assertEqual(a, b, [msg])
  • assertTrue(sentence, [msg])
  • assertFalse(sentence, [msg])

Test Suite

이제 test suite를 사용해 묶어서 돌리는 방법을 보자. 코드로 보는 것이 빠르다.

testSuite = unittest.TestSuite()
for testmethod in ('testsum1', 'testsum2'):
    testSuite.addTest(ModuleTest1(testmethod))
runner = unittest.TextTestRunner()
runner.run(testSuite)

테스트케이스를 저렇게 하나씩 넣는게 귀찮지.. class에서 함수를 긁어올 수는 없을까?해서 만들어보았다.

다음 코드는 class에서 test가 들어간 function들을 읽어오는 코드이다.

from types import FunctionType

class ModuleTest(unittest.TestCase):
    def testA(self):
            self.assertEqual(True,True)


for key, value in ModuleTest.__dict__.items():
    if 'test' in key and isinstance(value, FunctionType):
            print key
# testA

Code Coverage

위에서는 unittest를 하는 방법을 알아보았다. 해당 unittest를 통하여 코드를 얼마나 커버하는지 알 수 있다면 코드 품질 관리가 훨씬 좋을 것이다.

기본 모듈은 못찾았는데 pip install coverage로 coverage를 잴 수 있다.

coverage help를 쳐보면 기본 사용방법이 나온다.

usage: coverage <command> [options] [args]

Commands:
    annotate    Annotate source files with execution information.
    combine     Combine a number of data files.
    erase       Erase previously collected coverage data.
    help        Get help on using coverage.py.
    html        Create an HTML report.
    report      Report coverage stats on modules.
    run         Run a Python program and measure code execution.
    xml         Create an XML report of coverage results.

돌려보기

아까 만든 unittest를 test.py라 가정하자. 그러면 한줄만 치면 된다.

coverage run test.py

결과보기

결과는 terminal, html 두가지정도 있다. (xml도 있지만 안해봄..)

terminal 결과

coverage report로 볼 수 있다. 간단히 볼 때 좋다. 그러나 어떤 코드가 uncover인지 알고싶다면 html로 결과를 보는 것이 좋다.

Name                                          Stmts   Miss  Cover
-----------------------------------------------------------------
hulkFlaskFunctions/__init__.py                    0      0   100%
hulkFlaskFunctions/flaskPrettyPrinter.py          6      0   100%
hulkFlaskFunctions/hulkBaseValidate.py           13      3    77%
hulkFlaskFunctions/hulkConverterUnitTest.py      32      3    91%
hulkFlaskFunctions/hulkEquationConverter.py      45      6    87%
hulkFlaskFunctions/hulkPostCalculate.py          48      3    94%
...
hulkMathUtil/hulkSubstitute.py                   21      0   100%
hulkUnittest.py                                 231      4    98%
-----------------------------------------------------------------
TOTAL                                          1540    148    90%

html 결과

coverage html을 수행하면 htmlcov란 폴더가 생긴다. 이 폴더 안의 index.html을 열면 html 결과를 볼 수 있다.

소스를 누르면 다음처럼 커버가 안된 부분이 빨간색으로 나온다.

스크린샷 2016-10-28 오후 1.52.35.png

coverage에서 코드 빼기

코드에서 절대 타지 않는 부분이 있을 수 있다. 예를들면 python3, python2 호환코드라면 python3로 돌리면 2용 코드들이 돌아가지 않는다. 또는 오픈소스를 사용했는데 오픈소스의 커버리지는 재기 싫을 수 있다. 이를 위해 두가지 방법이 존재한다.

  • code의 일부분을 coverage에서 제외하기
  • 특정 directory를 coverage에서 제외하기

code의 일부분을 coverage에서 제외하기

# pragma: no cover 주석을 달면 해당 branch는 커버리지에서 제외한다.

class MyPrettyPrinter(pprint.PrettyPrinter):

    def format(self, _object, context, maxlevels, level):
        if sys.version_info[0] == 2:
            if isinstance(_object, unicode):  # pragma: no cover
                return "'%s'" % _object.encode('utf8'), True, False
            elif isinstance(_object, str):  # pragma: no cover
                _object = unicode(_object, 'utf8')
                return "'%s'" % _object.encode('utf8'), True, False
        return pprint.PrettyPrinter.format(self,
                                           _object,
                                           context,
                                           maxlevels,
                                           level)

위의 코드를 돌린 결과를 html로 보면

스크린샷 2016-10-28 오후 1.57.54.png

이렇게 회색 처리가 되면서 coverage에서 빠지게 된다.

특정 directory를 coverage에서 제외하기

정확한 config file을 알고싶진 않아서 방법만 제시한다.

다음처럼 [run] 부분에 omit = path를 넣는다.

그리고 file로 저장한다 (예제에서는 coverage_config)

[run]
omit = hulkFlaskFunctions/latex2sympy/*
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    pragma: no cover

    # Don't complain about missing debug-only code:
    def __repr__
    if self\.debug

    # Don't complain if tests don't hit defensive assertion code:
    raise AssertionError
    raise NotImplementedError

    # Don't complain if non-runnable code isn't run:
    if 0:
    if __name__ == .__main__.:

ignore_errors = True

[html]
directory = coverage_html_report

coverage run --rcfile=coverage_config test.py로 돌리면 hulkFlaskFunctions/latex2sympy/*에 해당하는 모든 소스가 coverage에서 무시된다.

참조

class attribute v.s. instance attribute

|

class attribute V.S. instance attribute

class Service(object):
    data = []

    def __init__(self, other_data):
        self.other_data = other_data
    ...

class attribute

위의 dataclass attirubte이며, []로 초기화 된 것이다.

동작 방식

class MyClass(object):
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

위의 클래스를 가정하고 다음을 실행해보자

foo = MyClass(2)
bar = MyClass(3)

foo.class_var, foo.i_var
## 1, 2
bar.class_var, bar.i_var
## 1, 3
MyClass.class_var ## <— This is key
## 1

MyClass.__dict__
'''
dict_proxy({'__module__': '__main__', 
            '__doc__': None, 
            '__dict__': <attribute '__dict__' of 'MyClass' objects>, 
            '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, 
            'class_var': 2, 
            '__init__': <function __init__ at 0x10895e398>})
'''

위에서 foo.class_var를 찾으면

  • instance attribute에서 찾는다.
  • 있으면 return
  • 없으면 class attribute에서 찾는다.
  • 있으면 return
  • 없으면 throw error
foo = MyClass(2)
foo.class_var
## 1
MyClass.class_var = 2
foo.class_var
## 2

class attribute를 바꾸면 instance에서 접근해도 모두 바뀐다.

foo = MyClass(2)
foo.class_var
## 1
foo.class_var = 2
foo.class_var
## 2
MyClass.class_var
## 1

instance에서 immutable한 class attribute를 바꾸려고하면 해당 instance만 이 attribute를 override한다.

class Service(object):
    data = []

    def __init__(self, other_data):
        self.other_data = other_data
    ...

s1 = Service(['a', 'b'])
s2 = Service(['c', 'd'])

s1.data.append(1)

s1.data
## [1]
s2.data
## [1]

s2.data.append(2)

s1.data
## [1, 2]
s2.data
## [1, 2]

mutable한 class attribute의 경우에는 위와 같이 instance가 override를 하지 않으므로 안쓰는 것이 좋다.

그럼 class attribute는 언제 씀?

constant 저장

class Circle(object):
    pi = 3.14159

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return Circle.pi * self.radius * self.radius

Circle.pi
## 3.14159

c = Circle(10)
c.pi
## 3.14159
c.area()
## 314.159

default value 저장

예를 들면, Queue를 만드는데 최대 갯수를 기본 10개로 한다던가…

class의 모든 instance의 정보 조회!

아까 말한 단점인데 패턴으로 쓰면 좋다.

class Person(object):
    all_names = []

    def __init__(self, name):
        self.name = name
        Person.all_names.append(name)

joe = Person('Joe')
bob = Person('Bob')
print Person.all_names
## ['Joe', 'Bob']

MVC pattern

|

MVC 패턴이란?

View, Controller, Model을 분리시켜서 재활용성을 높이는 것이 초점.

그림은 다음과 같다.

MVC

구성 요소

Model

data와 logic을 직접적으로 다루는 역할.

View

정보의 나타내는 뷰.

Controller

input을 받고 Model과 View에 필요한 요청을 함.

기본적인 골자는 위와 같으며 Model, View간의 의존성이 낮을 수록 좋다.

code

간단하게 짜본 코드를 살펴본다.

# Model.py
from functools import reduce
from typing import List

class dataSchema(object):
    def __init__(self, name: str, age: int):
        self.setProperty(name, age)

    def setProperty(self, name: str, age: int):
        self.name = name
        self.age  = age

    def getProperty(self):
        if self.age == -1:
            raise Exception('not setted.')
        return {'name': self.name, 'age': self.age}

    def __str__(self):
        return "(name : {}, age : {})".format(self.name, self.age)

    def __repr__(self):
        return "{}(\"{}\", {})".format(self.__class__.__name__, self.name, self.age)


class Model(object):
    def __init__(self):
        self.data = []

    def append(self, item : dataSchema):
        self.data.append(item)

    def getDataList(self) -> List[dataSchema]:
        return self.data

    def getTotalAge(self) -> int:
        def getAge(data):
            return data.getProperty()['age']
        
        totalAge = reduce(lambda x, y: x+y, map(getAge,self.data))
        return totalAge


def makeAndGetDummyData():
    '''
    실험을 위한 dummy data
    '''
    a = dataSchema("OHS", 28)
    b = dataSchema("HYJ", 28)
    return [a, b]
# View.py
from typing import List


class View:
    def __init__(self):
        pass

    def viewTotalAgeAndCount(self, totAge:int, count:int) -> None:
        print("###### totalAge ######\n{}\n#### number of data ####\n{}".format(totAge, count))
    def viewDataList(self, nameList: List[str], ageList: List[int]) -> None:
        if len(nameList) != len(ageList):
            raise Exception('the length of nameList must be same with the one of ageList.')
        print("name\tage")
        print("----------------")
        for name, age in zip(nameList, ageList):
            print("{}\t{}".format(name, age))
# Controller.py
from Model import Model, makeAndGetDummyData
from View import View
from functools import reduce
from typing import List


class Controller(object):
    def __init__(self):
        self.model = Model()
        dummyDataList = makeAndGetDummyData()
        for d in dummyDataList:
            self.model.append(d)
        self.view = View()

    def touchDataList(self) -> None:
        dataList = self.model.getDataList()

        nameList = [d.getProperty()["name"] for d in dataList]
        ageList = [d.getProperty()["age"] for d in dataList]

        self.view.viewDataList(nameList, ageList)

    def touchTotalAgeAndCount(self) -> None:
        totalAge = self.model.getTotalAge()
        dataLen = len(self.model.getDataList())

        self.view.viewTotalAgeAndCount(totalAge, dataLen)
# mvc.py
from Controller import Controller
if __name__ == '__main__':
    controller = Controller()

    controller.touchDataList()
    controller.touchTotalAgeAndCount()

코드를 보면 Controller에서 dummyData를 넣는 것을 볼 수 있다. 이는 data가 존재하지 않기에 들어간 코드이며, 실제로는 DB에서 가져오거나 하겠지…

cProfile 사용법

|

TOC

  • cProfile
  • memory profiler

    cProfile

간단 사용 설명서

요거 돌리면 된다. python -m cProfile -s tottime <myScript> <argumentList> > output

options

python -m cProfile [-s option]<myScript.py> > output 저기에 들어가는게 option!

options Description
‘calls’ call count
‘cumulative’ cumulative time
‘cumtime’ cumulative time
‘file’ file name
‘filename’ file name
‘module’ file name
‘ncalls’ call count
‘pcalls’ primitive call count
‘line’ line number
‘name’ function name
‘nfl’ name/file/line
‘stdname’ standard name
‘time’ internal time
‘tottime’ internal time

memory profiler

설치

pip install memory_profiler
pip install psutil

psutilmemory_profiler의 성능 향상을 위해 설치한다고 한다.

간단 사용

내가 프로파일링하고싶은 함수 위에 @profile 데코레이터를 넣는다. 그 후 python -m memory_profiler <myScript> <argumentList> 를 실행하면 그 함수의 라인 별로 memory usage가 나오는데 20배정도 느려지니 감안하고 기다리길…

Python 기초 - codecademy

|

codecademy의 python쪽 내용들을 정리해놓은 문서!

Variables and data types

welcome

print "Hello world"

variables and boolean

myVar = 5
myBool = True # or False
myFloat = 1.23

print myVar

Whitespace & statements

python에서 white space는 structure code를 위해 쓰인다.

IndentationError: unindent does not match any outer indentation level : 한 file에서는 tab과 space를 혼용해서는 안된다.

def spam():
    eggs = 12
    return eggs

print spam()

Comments

Single line comments

myVar = 4 # 이것은 주석이다

Multi line comments

"""이것은 모두 주석이다.
이것도 주석이다.
"""
myVar = 1

Math operation

Math

addition = 72 + 23
subtraction = 108 - 204
multiplication = 108 * 0.5
division = 108 / 9

Exponentiation & Modulo

eggs = 10 ** 2
print eggs

spam = 3 % 2
print eggs

Strings

Strings

name = "Ryan"
age  = "19"
food = "cheese"

escape = 'There\'s a snake in my boot!'

Access by index

"""
string "PYTHON" 은 6개의 문자를 가지고있다.

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5
"""
fifth_letter = "MONTY"[4]

print fifth_letter

String methods

len( string )

parrot = "Norwegian Blue"
print len(parrot)

string.lower()

parrot = "Norwegian Blue"
print parrot.lower()

string.upper()

parrot = "norwegian blue"
print parrot.upper()

toString str(var)

pi = 3.14
print str(pi)

Printing

print "Monty Python"
###
the_machine_goes = "Ping!"

print the_machine_goes
###
print "Spam " + "and " + "eggs" # string concatenation
###
pi = 3.14
print "The value of pi is around " + str(pi)

String formatting with %

string_1 = "Camelot"
string_2 = "place"

print "Let's not go to %s. 'Tis a silly %s." % (string_1, string_2)
name = raw_input("What is your name?")
quest = raw_input("What is your quest?")
color = raw_input("What is your favorite color?")

print "Ah, so your name is %s, your quest is %s, " \
"and your favorite color is %s." % (name, quest, color)

Date and time

from datetime import datetime
date = datetime.now()
print date

type(date) is datetime # True
type(date) is int      # False
type(date) is str      # False

Extracting information

from datetime import datetime

now = datetime.now()
print now.year
print now.month
print now.day
from datetime import datetime
now = datetime.now()

print '%s/%s/%s' % (now.year, now.month, now.day)
from datetime import datetime
now = datetime.now()

print '%s:%s:%s' % (now.hour, now.minute, now.second)

Conditionals & Control Flow

Comparators

Name Description
== Equal to
!= Not equal to
< Less than
<= Less than or equal to
> Greater than
>= Greater than or equal to

And Or Not

false = True and False
true  = True or False
notTrue = not True
  1. not is evaluated first;
  2. and is evaluated next;
  3. or is evaluated last.

걍 괄호를 쓰는게 낫지…


If else

answer = "Left"

if answer == "Left":
    print "The answer is Left"
elif answer == "Right":
    print "The answer is Right"
else:
    print "The answer is %s" % answer
def greater_less_equal_5(num):
    if num > 5:
        return 1
    elif num < 5:          
        return -1
    else:
        return 0

print greater_less_equal_5(4)
print greater_less_equal_5(5)
print greater_less_equal_5(6)

Testing…

original = raw_input("Enter a word:")

string.isalpha() : string이 alphabet으로 이루어져있으면 True, 아니면 False return.

# word를 받아서 맨 앞의 글자를 뒤로 보내고 ay를 붙인다.
pyg = 'ay'

original = raw_input('Enter a word:')

if len(original) > 0 and original.isalpha():
    word = original.lower()
    first = word[0]
    new_word = word[1:len(word)] + first + pyg
    print new_word
else:
    print 'empty'

Functions

def tax(bill):
    """Adds 8% tax to a restaurant bill."""
    bill *= 1.08
    print "With tax: %f" % bill
    return bill

def tip(bill):
    """Adds 15% tip to a restaurant bill."""
    bill *= 1.15
    print "With tip: %f" % bill
    return bill

meal_cost = 100
meal_with_tax = tax(meal_cost)
meal_with_tip = tip(meal_with_tax)

return하는 값이 다른 type이라도 가능하다.

def cube(number):
    return number**3

def by_three(number):
    if number % 3 == 0:
        return cube(number)
    else:
        return False

Importing modules

import math

print math.sqrt(25)
from math import sqrt

print sqrt(25)

View contents of the module

import math            # Imports the math module
everything = dir(math) # Sets everything to a list of things from math
print everything       # Prints 'em all!

Built-in Functions

abs(), min(), max()

def biggest_number( *args ): #argument의 list...
    print max( args )
    return max( args )

def smallest_number( *args ):
    print min( args )
    return min( args )

def distance_from_zero( arg ):
    print abs( arg )
    return abs( arg )


biggest_number( -10,-5,5,10 )
smallest_number( -10,-5,5,10 )
distance_from_zero( -10 )

type()

print type(53)
print type(1.23)
print type("Hello")

type(53) is int      # True
type(53) is str      # False

Exam

def hotel_cost(nights):
    return 140 * nights

def plane_ride_cost(city):
    if city == "Charlotte":
        return 183
    elif city == "Tampa":
        return 220
    elif city == "Pittsburgh":
        return 222
    else:
        return 475

def rental_car_cost(days):
    cost = 40 * days;

    if days >= 7:
        cost -= 50
    elif days>=3:
        cost -=20
    return cost

def trip_cost( city,
               days,
               spending_money ):
    return rental_car_cost(days) +
           hotel_cost(days) +
           plane_ride_cost(city) +
           spending_money

print trip_cost( "Los Angeles", 5, 600 )

List

List는 Array와 비슷한 datatype이다. 다만 element들이 다른 datatype이어도 괜찮다.

Length of list

zoo_animals = ["pangolin", "cassowary", "sloth", "tiger" ];
# One animal is missing!

if len(zoo_animals) > 3:
	print "The first animal at the zoo is the " + zoo_animals[0]
	print "The second animal at the zoo is the " + zoo_animals[1]
	print "The third animal at the zoo is the " + zoo_animals[2]
	print "The fourth animal at the zoo is the " + zoo_animals[3]

List append

빈 List 만들기 : empty_list = []

suitcase = []
suitcase.append("sunglasses")

List slicing

list[a:b] 는 a <= x < b인 x set을 return한다.

suitcase = ["sunglasses", "hat", "passport", "laptop", "suit", "shoes"]

first  = suitcase[0:2]  # The first and second items (index zero and one)
middle = suitcase[2:4]  # Third and fourth items (index two and three)
last   = suitcase[4:6]  # The last two items (index four and five)

string[0:3]도 마찬가지이다..

List index

animals = ["aardvark", "badger", "duck", "emu", "fennec fox"]
duck_index = animals.index("duck")  
print duck_index

List insert

animals = ["aardvark", "badger", "duck", "emu", "fennec fox"]
animals.insert(2,"cobra")
print animals

List remove

backpack = ['xylophone', 'dagger', 'tent', 'bread loaf']
backpack.remove('dagger')
print backpack

Looping with list

my_list = [1,9,3,8,5,7]

for number in my_list:
    # Your code here
    print 2*number

Sort list

start_list = [5, 3, 1, 2, 4]
start_list.sort()

print start_list

Dictionary

Dictionary는 key와 value의 쌍으로 이루어져있다.

residents = {'Puffin' : 104, 'Sloth' : 105, 'Burmese Python' : 106}와 같이 정의한다. key에 해당하는 value를 얻어오고 싶으면 residents['Puffin']으로 얻어올 수 있다.

Empty dictionary and adding an item

menu = {} # Empty dictionary
menu['Chicken Alfredo'] = 14.50 # Adding new key-value pair

Delete and update items with dictionary

# key - animal_name : value - location
zoo_animals = { 'Unicorn' : 'Cotton Candy House',
'Sloth' : 'Rainforest Exhibit',
'Bengal Tiger' : 'Jungle House',
'Atlantic Puffin' : 'Arctic Exhibit',
'Rockhopper Penguin' : 'Arctic Exhibit'}
# A dictionary (or list) declaration may break across multiple lines

# Removing the items.
del zoo_animals['Unicorn']
del zoo_animals['Sloth']
del zoo_animals['Bengal Tiger']
# update the item.
zoo_animals['Rockhopper Penguin'] = 'Nowhere'

print zoo_animals

Exam

inventory = {
    'gold' : 500,
    'pouch' : ['flint', 'twine', 'gemstone'], # Assigned a new list to 'pouch' key
    'backpack' : ['xylophone','dagger', 'bedroll','bread loaf']
}

# Adding a key 'burlap bag' and assigning a list to it
inventory['burlap bag'] = ['apple', 'small ruby', 'three-toed sloth']

# Sorting the list found under the key 'pouch'
inventory['pouch'].sort()

# Your code here
inventory['pocket'] = ['seashell', 'strange berry', 'lint']

inventory['backpack'].sort()
inventory['backpack'].remove('dagger');
inventory['gold'] += 50

Class

class Fruit(object):  
    """A class that makes various tasty fruits."""
    def __init__(self, name, color, flavor, poisonous): # constructor와 같은 듯...
        self.name = name
        self.color = color
        self.flavor = flavor
        self.poisonous = poisonous

    def description(self):                              # method들을 이렇게 정의할 수 있다.
        print "I'm a %s %s and I taste %s." % (self.color, self.name, self.flavor)

    def is_edible(self):
        if not self.poisonous:
            print "Yep! I'm edible."
        else:
            print "Don't eat me! I am super poisonous."

lemon = Fruit("lemon", "yellow", "sour", False)

lemon.description()
lemon.is_edible()

results :

I'm a yellow lemon and I taste sour.
Yep! I'm edible.
None

Class Syntax

class NewClass(object): # object를 inherit한다.
    # Class magic here

convention에 따라 class name은 대문자로 시작한다.

Class methods

init()

__init__() 함수는 정의한 class를 만들 때 initialize하기 위해 쓰인다. 또한 모든 method는 항상 self라는 argument를 동반한다. self는 내가 만들 object를 refer한다.

class Animal(object):
    def __init__( self ):
        pass

또한 그 뒤에 다른 argument를 넣을 수 있다.

class Animal(object):
    def __init__( self, name ):
        self.name = name

class member variable 접근

class Animal(object):
    def __init__( self, name ):
        self.name = name

zebra = Animal("Jeffrey")   # 이런 식으로 Instance를 만들 수 있다.

print zebra.name            # 이런 식으로 member variable에 접근가능하다.

Member variable 좀더…

class Animal(object):
    """Makes cute animals."""
    is_alive = True             # 요것도 member variable이다!
    def __init__(self, name, age):
        self.name = name
        self.age = age

zebra = Animal("Jeffrey", 2)
giraffe = Animal("Bruce", 1)
panda = Animal("Chad", 7)

print zebra.name, zebra.age, zebra.is_alive
print giraffe.name, giraffe.age, giraffe.is_alive
print panda.name, panda.age, panda.is_alive

results :

Jeffrey 2 True
Bruce 1 True
Chad 7 True
class ShoppingCart(object):
    """Creates shopping cart objects
    for users of our fine website."""
    items_in_cart = {}
    def __init__(self, customer_name):
        self.customer_name = customer_name

    def add_item(self, product, price):
        """Add product to the cart."""
        if not product in self.items_in_cart:
            self.items_in_cart[product] = price
            print product + " added."
        else:
            print product + " is already in the cart."

    def remove_item(self, product):
        """Remove product from the cart."""
        if product in self.items_in_cart:
            del self.items_in_cart[product]
            print product + " removed."
        else:
            print product + " is not in the cart."

my_cart = ShoppingCart( "durotan" )
my_cart.add_item( "sazabi", 80000 )