<filename>py3book31/py31eg/BikeStock.py
#!/usr/bin/env python3
# Copyright (c) 2008-11 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. It is provided for educational
# purposes and is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

"""
>>> import os
>>> import tempfile
>>> bike_file = os.path.join(tempfile.gettempdir(), "bikes.dat")
>>> if os.path.exists(bike_file): os.remove(bike_file)

>>> bike_data = []
>>> bike_data.append(('REFK2', 'Reflex Kalahari', 5, 200.97))
>>> bike_data.append(('REFT1', 'Reflex Tempus', 4, 200.97))
>>> bike_data.append(('UNISTOW', 'Universal Stowaway', 1, 203.00))
>>> bike_data.append(('REFONA', "Reflex Out 'n' About", 0, 213.15))
>>> bike_data.append(('B4U16RS', 'Bicycles4U 16/6/RS  ', 0, 223.30))
>>> bike_data.append(('B4U20', 'Bicycles4U 20/6', 9, 223.30))
>>> bike_data.append(('B4U20MTB', 'Bicycles4U 20/6/MTB', 5, 223.30))
>>> bike_data.append(('REFA3', 'Reflex Axiom 3', 15, 223.30))
>>> bike_data.append(('ASBC', 'AS Bikes Compact', 22, 243.60))
>>> bike_data.append(('AMMC', 'Ammaco Commuter', 4, 259.84))
>>> bike_data.append(('AMMP5', 'Ammaco Pakka Mk 5', 7, 259.84))
>>> bike_data.append(('B4U20RS', 'Bicycles4U 20/6/RS', 3, 263.90))
>>> bike_data.append(('COM16', 'Compass 16"', 2, 263.90))
>>> bike_data.append(('ASBC+', 'AS Bikes Compact Plus', 11, 284.20))
>>> bike_data.append(('TIGB', 'Tiger Bikes', 0, 284.20))
>>> bike_data.append(('ASBEX', 'AS Bikes Explorer', 2, 304.50))
>>> bike_data.append(('GEKKO', 'Gekko', 6, 304.50))
>>> bike_data.append(('PROBE', 'Probike Enfold', 4, 304.50))
>>> bike_data.append(('SAMHDXM', 'Samchuly Haro DX MTB', 5, 304.50))
>>> bike_data.append(('SINAB', 'Sinclair A-Bike', 3, 304.50))
>>> bike_data.append(('FALOM', 'Falcon Optima Mayfair', 2, 324.80))
>>> bike_data.append(('RALPARK', 'Raleigh Parkway', 1, 324.80))
>>> bike_data.append(('SAXS', 'Saxon Safari', 2, 324.80))
>>> bike_data.append(('CLACHA', 'Classic Chatsworth', 0, 328.86))
>>> bike_data.append(('ASBE+', 'AS Bikes Explorer Plus', 3, 345.10))
>>> bike_data.append(('RALPROM', 'Raleigh Promenade', 2, 345.10))
>>> bike_data.append(('VIKBS', 'Viking Bikes Safari', 1, 345.10))
>>> bike_data.append(('AMMT+C', 'Ammaco Town & Country', 0, 353.22))
>>> bike_data.append(('AMMCT', 'Ammaco Cruiser Tandem 16"', 0, 355.25))
>>> bike_data.append(('AMMMON', 'Ammaco Montreal', 4, 355.25))
>>> bike_data.append(('MSGENIE', 'Mission Space Genie', 4, 355.25))
>>> bike_data.append(('TRANSS', 'Transair Sea Sure', 3, 355.25))
>>> bike_data.append(('DAHSP', 'Dahon Sweet Pea', 1, 363.37))
>>> bike_data.append(('SALEASY', 'Salcano Easy', 0, 365.40))
>>> bike_data.append(('CLAMEL', 'Classic Melbourne', 1, 379.61))
>>> bike_data.append(('VENTGL', 'Ventura Go Lite', 1, 383.67))

>>> bicycles = BikeStock(bike_file)
>>> for bike in bike_data:
...     bicycles.append(Bike(*bike))
>>> bicycles["VIKBS"].name
'Viking Bikes Safari'
>>> ok = []
>>> value = 0.0
>>> for i, bike in enumerate(bicycles):
...     ok.append(bike.quantity == bike_data[i][2])
...     value += bike.value
>>> all(ok), "{0:.2f}".format(round(value, 2))
(True, '35969.57')
>>> bicycles["SALEASY"].name
'Salcano Easy'
>>> bicycles.change_name("SALEASY", "Salcano EZ")
True
>>> bicycles["SALEASY"].name
'Salcano EZ'
>>> total = 0
>>> for bike in bicycles:
...     if bike.identity.startswith("B4U"):
...         total += bike.quantity
>>> total
17
>>> total = 0
>>> for bike in bicycles:
...     if bike.identity.startswith("B4U"):
...         if bicycles.increase_stock(bike.identity, 2):
...             total += bicycles[bike.identity].quantity
...         else:
...             print("error", bike)
>>> total
25
>>> value = 0.0
>>> count = 0
>>> for bike in bicycles:
...     value += bike.value
...     count += 1
>>> "{0:.2f}".format(round(value, 2)), count, count == len(bike_data)
('37837.17', 36, True)
>>> bicycles["CLAMEL"].name
'Classic Melbourne'
>>> del bicycles["UNISTOW"]
>>> value = 0.0
>>> count = 0
>>> for bike in bicycles:
...     value += bike.value
...     count += 1
>>> "{0:.2f}".format(round(value, 2)), count
('37634.17', 35)
>>> bicycles["CLAMEL"].name
'Classic Melbourne'
>>> bicycles.append(Bike('UNISTOW', 'Universal Stowaway', 1, 203.00))
>>> bicycles.close()

>>> bicycles = BikeStock(bike_file)
>>> value = 0.0
>>> for bike in bicycles:
...     value += bike.value
>>> "{0:.2f}".format(round(value, 2))
'37837.17'
>>> bicycles.close()
>>> os.path.getsize(bike_file)
1836
>>> if os.path.exists(bike_file): os.remove(bike_file)
"""

import struct
import BinaryRecordFile


class Bike:

    def __init__(self, identity, name, quantity, price):
        assert len(identity) > 3, ("invalid bike identity '{0}'"
                                   .format(identity))
        self.__identity = identity
        self.name = name
        self.quantity = quantity
        self.price = price


    @property
    def identity(self):
        "The bike's identity"
        return self.__identity


    @property
    def name(self):
        "The bike's name"
        return self.__name

    @name.setter
    def name(self, name):
        assert len(name), "bike name must not be empty"
        self.__name = name


    @property
    def quantity(self):
        "How many of this bike are in stock"
        return self.__quantity

    @quantity.setter
    def quantity(self, quantity):
        assert 0 <= quantity, "quantity must not be negative"
        self.__quantity = quantity


    @property
    def price(self):
        "The bike's price"
        return self.__price

    @price.setter
    def price(self, price):
        assert 0.0 <= price, "price must not be negative"
        self.__price = price


    @property
    def value(self):
        "The value of these bikes in stock"
        return self.quantity * self.price


_BIKE_STRUCT = struct.Struct("<8s30sid")


def _bike_from_record(record):
    ID, NAME, QUANTITY, PRICE = range(4)
    parts = list(_BIKE_STRUCT.unpack(record))
    parts[ID] = parts[ID].decode("utf8").rstrip("\x00")
    parts[NAME] = parts[NAME].decode("utf8").rstrip("\x00")
    return Bike(*parts)


def _record_from_bike(bike):
    return _BIKE_STRUCT.pack(bike.identity.encode("utf8"),
                             bike.name.encode("utf8"),
                             bike.quantity, bike.price)


class BikeStock:

    def __init__(self, filename):
        self.__file = BinaryRecordFile.BinaryRecordFile(filename,
                                                _BIKE_STRUCT.size)
        self.__index_from_identity = {}
        for index in range(len(self.__file)):
            record = self.__file[index]
            if record is not None:
                bike = _bike_from_record(record)
                self.__index_from_identity[bike.identity] = index


    def close(self):
        "Compacts and closes the file"
        self.__file.inplace_compact()
        self.__file.close()


    def append(self, bike):
        "Adds a new bike to the stock"
        index = len(self.__file)
        self.__file[index] = _record_from_bike(bike)
        self.__index_from_identity[bike.identity] = index
        

    def __delitem__(self, identity):
        "Deletes the stock record for the specified bike"
        del self.__file[self.__index_from_identity[identity]]
        del self.__index_from_identity[identity]


    def __getitem__(self, identity):
        "Retrieves the stock record for the specified bike"
        record = self.__file[self.__index_from_identity[identity]]
        return None if record is None else _bike_from_record(record)


    def __change_bike(self, identity, what, value):
        index = self.__index_from_identity[identity]
        record = self.__file[index]
        if record is None:
            return False
        bike = _bike_from_record(record)
        if what == "price" and value is not None and value >= 0.0:
            bike.price = value
        elif what == "name" and value is not None:
            bike.name = value
        else:
            return False
        self.__file[index] = _record_from_bike(bike)
        return True

    change_name = lambda self, identity, name: self.__change_bike(
                                            identity, "name", name)
    change_name.__doc__ = "Changes the bike's name"

    change_price = lambda self, identity, price: self.__change_bike(
                                            identity, "price", name)
    change_price.__doc__ = "Changes the bike's price"


    def __change_stock(self, identity, amount):
        index = self.__index_from_identity[identity]
        record = self.__file[index]
        if record is None:
            return False
        bike = _bike_from_record(record)
        bike.quantity += amount
        self.__file[index] = _record_from_bike(bike)
        return True
        
    increase_stock = (lambda self, identity, amount:
                                self.__change_stock(identity, amount))
    increase_stock.__doc__ = ("Increases the stock held for the "
                              "specified bike by by the given amount")

    decrease_stock = (lambda self, identity, amount:
                                self.__change_stock(identity, -amount))
    decrease_stock.__doc__ = ("Decreases the stock held for the "
                              "specified bike by by the given amount")

        
    def __iter__(self):
        for index in range(len(self.__file)):
            record = self.__file[index]
            if record is not None:
                yield _bike_from_record(record)


if __name__ == "__main__":
    import doctest
    doctest.testmod()

<filename>py3book31/py31eg/BikeStock.py
#!/usr/bin/env python3
# Copyright (c) 2008-11 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. It is provided for educational
# purposes and is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

"""
>>> import os
>>> import tempfile
>>> bike_file = os.path.join(tempfile.gettempdir(), "bikes.dat")
>>> if os.path.exists(bike_file): os.remove(bike_file)

>>> bike_data = []
>>> bike_data.append(('REFK2', 'Reflex Kalahari', 5, 200.97))
>>> bike_data.append(('REFT1', 'Reflex Tempus', 4, 200.97))
>>> bike_data.append(('UNISTOW', 'Universal Stowaway', 1, 203.00))
>>> bike_data.append(('REFONA', "Reflex Out 'n' About", 0, 213.15))
>>> bike_data.append(('B4U16RS', 'Bicycles4U 16/6/RS  ', 0, 223.30))
>>> bike_data.append(('B4U20', 'Bicycles4U 20/6', 9, 223.30))
>>> bike_data.append(('B4U20MTB', 'Bicycles4U 20/6/MTB', 5, 223.30))
>>> bike_data.append(('REFA3', 'Reflex Axiom 3', 15, 223.30))
>>> bike_data.append(('ASBC', 'AS Bikes Compact', 22, 243.60))
>>> bike_data.append(('AMMC', 'Ammaco Commuter', 4, 259.84))
>>> bike_data.append(('AMMP5', 'Ammaco Pakka Mk 5', 7, 259.84))
>>> bike_data.append(('B4U20RS', 'Bicycles4U 20/6/RS', 3, 263.90))
>>> bike_data.append(('COM16', 'Compass 16"', 2, 263.90))
>>> bike_data.append(('ASBC+', 'AS Bikes Compact Plus', 11, 284.20))
>>> bike_data.append(('TIGB', 'Tiger Bikes', 0, 284.20))
>>> bike_data.append(('ASBEX', 'AS Bikes Explorer', 2, 304.50))
>>> bike_data.append(('GEKKO', 'Gekko', 6, 304.50))
>>> bike_data.append(('PROBE', 'Probike Enfold', 4, 304.50))
>>> bike_data.append(('SAMHDXM', 'Samchuly Haro DX MTB', 5, 304.50))
>>> bike_data.append(('SINAB', 'Sinclair A-Bike', 3, 304.50))
>>> bike_data.append(('FALOM', 'Falcon Optima Mayfair', 2, 324.80))
>>> bike_data.append(('RALPARK', 'Raleigh Parkway', 1, 324.80))
>>> bike_data.append(('SAXS', 'Saxon Safari', 2, 324.80))
>>> bike_data.append(('CLACHA', 'Classic Chatsworth', 0, 328.86))
>>> bike_data.append(('ASBE+', 'AS Bikes Explorer Plus', 3, 345.10))
>>> bike_data.append(('RALPROM', 'Raleigh Promenade', 2, 345.10))
>>> bike_data.append(('VIKBS', 'Viking Bikes Safari', 1, 345.10))
>>> bike_data.append(('AMMT+C', 'Ammaco Town & Country', 0, 353.22))
>>> bike_data.append(('AMMCT', 'Ammaco Cruiser Tandem 16"', 0, 355.25))
>>> bike_data.append(('AMMMON', 'Ammaco Montreal', 4, 355.25))
>>> bike_data.append(('MSGENIE', 'Mission Space Genie', 4, 355.25))
>>> bike_data.append(('TRANSS', 'Transair Sea Sure', 3, 355.25))
>>> bike_data.append(('DAHSP', 'Dahon Sweet Pea', 1, 363.37))
>>> bike_data.append(('SALEASY', 'Salcano Easy', 0, 365.40))
>>> bike_data.append(('CLAMEL', 'Classic Melbourne', 1, 379.61))
>>> bike_data.append(('VENTGL', 'Ventura Go Lite', 1, 383.67))

>>> bicycles = BikeStock(bike_file)
>>> for bike in bike_data:
...     bicycles.append(Bike(*bike))
>>> bicycles["VIKBS"].name
'Viking Bikes Safari'
>>> ok = []
>>> value = 0.0
>>> for i, bike in enumerate(bicycles):
...     ok.append(bike.quantity == bike_data[i][2])
...     value += bike.value
>>> all(ok), "{0:.2f}".format(round(value, 2))
(True, '35969.57')
>>> bicycles["SALEASY"].name
'Salcano Easy'
>>> bicycles.change_name("SALEASY", "Salcano EZ")
True
>>> bicycles["SALEASY"].name
'Salcano EZ'
>>> total = 0
>>> for bike in bicycles:
...     if bike.identity.startswith("B4U"):
...         total += bike.quantity
>>> total
17
>>> total = 0
>>> for bike in bicycles:
...     if bike.identity.startswith("B4U"):
...         if bicycles.increase_stock(bike.identity, 2):
...             total += bicycles[bike.identity].quantity
...         else:
...             print("error", bike)
>>> total
25
>>> value = 0.0
>>> count = 0
>>> for bike in bicycles:
...     value += bike.value
...     count += 1
>>> "{0:.2f}".format(round(value, 2)), count, count == len(bike_data)
('37837.17', 36, True)
>>> bicycles["CLAMEL"].name
'Classic Melbourne'
>>> del bicycles["UNISTOW"]
>>> value = 0.0
>>> count = 0
>>> for bike in bicycles:
...     value += bike.value
...     count += 1
>>> "{0:.2f}".format(round(value, 2)), count
('37634.17', 35)
>>> bicycles["CLAMEL"].name
'Classic Melbourne'
>>> bicycles.append(Bike('UNISTOW', 'Universal Stowaway', 1, 203.00))
>>> bicycles.close()

>>> bicycles = BikeStock(bike_file)
>>> value = 0.0
>>> for bike in bicycles:
...     value += bike.value
>>> "{0:.2f}".format(round(value, 2))
'37837.17'
>>> bicycles.close()
>>> os.path.getsize(bike_file)
1836
>>> if os.path.exists(bike_file): os.remove(bike_file)
"""

import struct
import BinaryRecordFile


class Bike:

    def __init__(self, identity, name, quantity, price):
        assert len(identity) > 3, ("invalid bike identity '{0}'"
                                   .format(identity))
        self.__identity = identity
        self.name = name
        self.quantity = quantity
        self.price = price


    @property
    def identity(self):
        "The bike's identity"
        return self.__identity


    @property
    def name(self):
        "The bike's name"
        return self.__name

    @name.setter
    def name(self, name):
        assert len(name), "bike name must not be empty"
        self.__name = name


    @property
    def quantity(self):
        "How many of this bike are in stock"
        return self.__quantity

    @quantity.setter
    def quantity(self, quantity):
        assert 0 <= quantity, "quantity must not be negative"
        self.__quantity = quantity


    @property
    def price(self):
        "The bike's price"
        return self.__price

    @price.setter
    def price(self, price):
        assert 0.0 <= price, "price must not be negative"
        self.__price = price


    @property
    def value(self):
        "The value of these bikes in stock"
        return self.quantity * self.price


_BIKE_STRUCT = struct.Struct("<8s30sid")


def _bike_from_record(record):
    ID, NAME, QUANTITY, PRICE = range(4)
    parts = list(_BIKE_STRUCT.unpack(record))
    parts[ID] = parts[ID].decode("utf8").rstrip("\x00")
    parts[NAME] = parts[NAME].decode("utf8").rstrip("\x00")
    return Bike(*parts)


def _record_from_bike(bike):
    return _BIKE_STRUCT.pack(bike.identity.encode("utf8"),
                             bike.name.encode("utf8"),
                             bike.quantity, bike.price)


class BikeStock:

    def __init__(self, filename):
        self.__file = BinaryRecordFile.BinaryRecordFile(filename,
                                                _BIKE_STRUCT.size)
        self.__index_from_identity = {}
        for index in range(len(self.__file)):
            record = self.__file[index]
            if record is not None:
                bike = _bike_from_record(record)
                self.__index_from_identity[bike.identity] = index


    def close(self):
        "Compacts and closes the file"
        self.__file.inplace_compact()
        self.__file.close()


    def append(self, bike):
        "Adds a new bike to the stock"
        index = len(self.__file)
        self.__file[index] = _record_from_bike(bike)
        self.__index_from_identity[bike.identity] = index
        

    def __delitem__(self, identity):
        "Deletes the stock record for the specified bike"
        del self.__file[self.__index_from_identity[identity]]
        del self.__index_from_identity[identity]


    def __getitem__(self, identity):
        "Retrieves the stock record for the specified bike"
        record = self.__file[self.__index_from_identity[identity]]
        return None if record is None else _bike_from_record(record)


    def __change_bike(self, identity, what, value):
        index = self.__index_from_identity[identity]
        record = self.__file[index]
        if record is None:
            return False
        bike = _bike_from_record(record)
        if what == "price" and value is not None and value >= 0.0:
            bike.price = value
        elif what == "name" and value is not None:
            bike.name = value
        else:
            return False
        self.__file[index] = _record_from_bike(bike)
        return True

    change_name = lambda self, identity, name: self.__change_bike(
                                            identity, "name", name)
    change_name.__doc__ = "Changes the bike's name"

    change_price = lambda self, identity, price: self.__change_bike(
                                            identity, "price", name)
    change_price.__doc__ = "Changes the bike's price"


    def __change_stock(self, identity, amount):
        index = self.__index_from_identity[identity]
        record = self.__file[index]
        if record is None:
            return False
        bike = _bike_from_record(record)
        bike.quantity += amount
        self.__file[index] = _record_from_bike(bike)
        return True
        
    increase_stock = (lambda self, identity, amount:
                                self.__change_stock(identity, amount))
    increase_stock.__doc__ = ("Increases the stock held for the "
                              "specified bike by by the given amount")

    decrease_stock = (lambda self, identity, amount:
                                self.__change_stock(identity, -amount))
    decrease_stock.__doc__ = ("Decreases the stock held for the "
                              "specified bike by by the given amount")

        
    def __iter__(self):
        for index in range(len(self.__file)):
            record = self.__file[index]
            if record is not None:
                yield _bike_from_record(record)


if __name__ == "__main__":
    import doctest
    doctest.testmod()

#!/usr/bin/env python3 # Copyright (c) 2008-11 Qtrac Ltd. All rights reserved. # This program or module is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. It is provided for educational # purposes and is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. >>> import os >>> import tempfile >>> bike_file = os.path.join(tempfile.gettempdir(), "bikes.dat") >>> if os.path.exists(bike_file): os.remove(bike_file)  >>> bike_data = [] >>> bike_data.append(('REFK2', 'Reflex Kalahari', 5, 200.97)) >>> bike_data.append(('REFT1', 'Reflex Tempus', 4, 200.97)) >>> bike_data.append(('UNISTOW', 'Universal Stowaway', 1, 203.00)) >>> bike_data.append(('REFONA', "Reflex Out 'n' About", 0, 213.15)) >>> bike_data.append(('B4U16RS', 'Bicycles4U 16/6/RS  ', 0, 223.30)) >>> bike_data.append(('B4U20', 'Bicycles4U 20/6', 9, 223.30)) >>> bike_data.append(('B4U20MTB', 'Bicycles4U 20/6/MTB', 5, 223.30)) >>> bike_data.append(('REFA3', 'Reflex Axiom 3', 15, 223.30)) >>> bike_data.append(('ASBC', 'AS Bikes Compact', 22, 243.60)) >>> bike_data.append(('AMMC', 'Ammaco Commuter', 4, 259.84)) >>> bike_data.append(('AMMP5', 'Ammaco Pakka Mk 5', 7, 259.84)) >>> bike_data.append(('B4U20RS', 'Bicycles4U 20/6/RS', 3, 263.90)) >>> bike_data.append(('COM16', 'Compass 16"', 2, 263.90)) >>> bike_data.append(('ASBC+', 'AS Bikes Compact Plus', 11, 284.20)) >>> bike_data.append(('TIGB', 'Tiger Bikes', 0, 284.20)) >>> bike_data.append(('ASBEX', 'AS Bikes Explorer', 2, 304.50)) >>> bike_data.append(('GEKKO', 'Gekko', 6, 304.50)) >>> bike_data.append(('PROBE', 'Probike Enfold', 4, 304.50)) >>> bike_data.append(('SAMHDXM', 'Samchuly Haro DX MTB', 5, 304.50)) >>> bike_data.append(('SINAB', 'Sinclair A-Bike', 3, 304.50)) >>> bike_data.append(('FALOM', 'Falcon Optima Mayfair', 2, 324.80)) >>> bike_data.append(('RALPARK', 'Raleigh Parkway', 1, 324.80)) >>> bike_data.append(('SAXS', 'Saxon Safari', 2, 324.80)) >>> bike_data.append(('CLACHA', 'Classic Chatsworth', 0, 328.86)) >>> bike_data.append(('ASBE+', 'AS Bikes Explorer Plus', 3, 345.10)) >>> bike_data.append(('RALPROM', 'Raleigh Promenade', 2, 345.10)) >>> bike_data.append(('VIKBS', 'Viking Bikes Safari', 1, 345.10)) >>> bike_data.append(('AMMT+C', 'Ammaco Town & Country', 0, 353.22)) >>> bike_data.append(('AMMCT', 'Ammaco Cruiser Tandem 16"', 0, 355.25)) >>> bike_data.append(('AMMMON', 'Ammaco Montreal', 4, 355.25)) >>> bike_data.append(('MSGENIE', 'Mission Space Genie', 4, 355.25)) >>> bike_data.append(('TRANSS', 'Transair Sea Sure', 3, 355.25)) >>> bike_data.append(('DAHSP', 'Dahon Sweet Pea', 1, 363.37)) >>> bike_data.append(('SALEASY', 'Salcano Easy', 0, 365.40)) >>> bike_data.append(('CLAMEL', 'Classic Melbourne', 1, 379.61)) >>> bike_data.append(('VENTGL', 'Ventura Go Lite', 1, 383.67))  >>> bicycles = BikeStock(bike_file) >>> for bike in bike_data: ...     bicycles.append(Bike(*bike)) >>> bicycles["VIKBS"].name 'Viking Bikes Safari' >>> ok = [] >>> value = 0.0 >>> for i, bike in enumerate(bicycles): ...     ok.append(bike.quantity == bike_data[i][2]) ...     value += bike.value >>> all(ok), "{0:.2f}".format(round(value, 2)) (True, '35969.57') >>> bicycles["SALEASY"].name 'Salcano Easy' >>> bicycles.change_name("SALEASY", "Salcano EZ") True >>> bicycles["SALEASY"].name 'Salcano EZ' >>> total = 0 >>> for bike in bicycles: ...     if bike.identity.startswith("B4U"): ...         total += bike.quantity >>> total 17 >>> total = 0 >>> for bike in bicycles: ...     if bike.identity.startswith("B4U"): ...         if bicycles.increase_stock(bike.identity, 2): ...             total += bicycles[bike.identity].quantity ...         else: ...             print("error", bike) >>> total 25 >>> value = 0.0 >>> count = 0 >>> for bike in bicycles: ...     value += bike.value ...     count += 1 >>> "{0:.2f}".format(round(value, 2)), count, count == len(bike_data) ('37837.17', 36, True) >>> bicycles["CLAMEL"].name 'Classic Melbourne' >>> del bicycles["UNISTOW"] >>> value = 0.0 >>> count = 0 >>> for bike in bicycles: ...     value += bike.value ...     count += 1 >>> "{0:.2f}".format(round(value, 2)), count ('37634.17', 35) >>> bicycles["CLAMEL"].name 'Classic Melbourne' >>> bicycles.append(Bike('UNISTOW', 'Universal Stowaway', 1, 203.00)) >>> bicycles.close()  >>> bicycles = BikeStock(bike_file) >>> value = 0.0 >>> for bike in bicycles: ...     value += bike.value >>> "{0:.2f}".format(round(value, 2)) '37837.17' >>> bicycles.close() >>> os.path.getsize(bike_file) 1836 >>> if os.path.exists(bike_file): os.remove(bike_file)