最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to overpass test case on exercise in MOOC.fi? - Stack Overflow

programmeradmin5浏览0评论

Been studding python for a while now, did not ask anything since AI was useful for clarifying most of the simple stuff. In advancing Python programming course, in chapter 10, one of the last exercise was phonebook exercise as a final OOP exercise. My code has passed all of the test cases but on one I got stuck. I tried debugging for about 4h and could not figure out so I'm in doubt weather a test might be wrong since I'm not familiar with tests. Here is my entire OOP code followed by a test on which code fails with an output of a failed test. I would really apreciate if anyone could clarify my mistake.

class PhoneBook:
    def __init__(self):
        self.__persons = {}

    def add_number(self, name: str, number: str):
        if name in self.__persons:
            self.__persons[name].add_number(number)
        else:
            self.__persons[name] = Person(name=name, numbers=[number])

    def get_entry(self, name: str):
        # IMPORTANT this part bellof (2-3 lines) is problematic for the given test case...
        if name in self.__persons and not self.__persons[name].address():
            print(self.__persons[name].numbers()[0])
            print("address unknown")
        elif name in self.__persons and not self.__persons[name].numbers():
            print("number unknown")
            print(self.__persons[name].address())
        elif name not in self.__persons:
            print("address unknown")
            print("number unknown")
        else:
            for num in self.__persons[name].numbers():
                print(num)
            print(self.__persons[name].address())


    def add_address(self, name:str, address:str):
        if name in self.__persons:
            self.__persons[name].add_address(address)
        else:
            self.__persons[name] = Person(name = name, address=address)

    def all_entries(self):
        return self.__persons

class PhoneBookApplication:
    def __init__(self):
        self.__phonebook = PhoneBook()

    def help(self):
        print("commands: ")
        print("0 exit")
        print("1 add number")
        print("2 search")
        print("3 add address")

    def add_number(self):
        name = input("name: ")
        number = input("number: ")
        self.__phonebook.add_number(name, number)

    def search(self):
        name = input("name: ")
        self.__phonebook.get_entry(name)

    def add_address(self):
        name = input("name: ")
        address = input("address: ")
        if address:
            self.__phonebook.add_address(name, address)
        else:
            raise ValueError("Enter a valid address.")

    def execute(self):
        self.help()
        while True:
            print("")
            command = input("command: ")
            if command == "0":
                break
            elif command == "1":
                self.add_number()
            elif command == "2":
                self.search()
            elif command == "3":
                self.add_address()
            else:
                self.help()

class Person:
    def __init__(self, name:str, address:str = None, numbers:list = None):
        self.__name = name
        self.__address = address if address is not None else ""
        self.__numbers = numbers if numbers is not None else []

    def name(self):
        return self.__name

    def numbers(self):
        return self.__numbers

    def address(self):
        if self.__address:
            return self.__address
        else:
            return None

    def add_number(self, number:str):
        if number:
            self.__numbers.append(number)
        else:
            raise ValueError("Enter a valid number.")

    def add_address(self, address:str):
        if address:
            self.__address = address
        else:
            raise ValueError("Enter a valid address.")

    def __str__(self):
        return f"{self.numbers()}\n{self.address()}"


phonebook = PhoneBookApplication()
phonebook.execute()

TEST FAIL OUTPUT:

#IMPORTANT TEST FAIL OUTPUT:
# Test failed
# PhoneBook2_Part2Test: test_3_works_many_numbers
# The output of your program should be
# 040-999999
# with input
# 1
# Emilia
# 09-123456
# 1
# Emilia
# 040-999999
# 2
# Emilia
# 0
# Now the output was
# commands:
# 0 exit
# 1 add number
# 2 search
# 3 add address
#
# 09-123456
# address unknown

TEST ON WHICH CODE FAILS:

import unittest
from unittest.mock import patch

from tmc import points, reflect
from tmc.utils import load, load_module, reload_module, get_stdout, check_source
from functools import reduce
import os
import os.path
import textwrap
from random import choice, randint
from datetime import date, datetime, timedelta

def test_3_works_many_numbers(self):
    syote = ["1", "Emilia", "09-123456", "1", "Emilia", "040-999999", "2", "Emilia", "0"]
    with patch('builtins.input', side_effect=syote):
        try:
            reload_module(self.module)
        except:
            self.fail(f"Check that the program works with input\n{s(syote)}")

        output = get_stdout()
        expected = "09-123456"
        self.assertTrue(expected in output,
                        f"The output of your program should be\n{expected}\nwith input\n{s(syote)}\nNow the output was\n{output}")
        expected = "040-999999"
        self.assertTrue(expected in output,
                        f"The output of your program should be\n{expected}\nwith input\n{s(syote)}\nNow the output was\n{output}")

Been studding python for a while now, did not ask anything since AI was useful for clarifying most of the simple stuff. In advancing Python programming course, in chapter 10, one of the last exercise was phonebook exercise as a final OOP exercise. My code has passed all of the test cases but on one I got stuck. I tried debugging for about 4h and could not figure out so I'm in doubt weather a test might be wrong since I'm not familiar with tests. Here is my entire OOP code followed by a test on which code fails with an output of a failed test. I would really apreciate if anyone could clarify my mistake.

class PhoneBook:
    def __init__(self):
        self.__persons = {}

    def add_number(self, name: str, number: str):
        if name in self.__persons:
            self.__persons[name].add_number(number)
        else:
            self.__persons[name] = Person(name=name, numbers=[number])

    def get_entry(self, name: str):
        # IMPORTANT this part bellof (2-3 lines) is problematic for the given test case...
        if name in self.__persons and not self.__persons[name].address():
            print(self.__persons[name].numbers()[0])
            print("address unknown")
        elif name in self.__persons and not self.__persons[name].numbers():
            print("number unknown")
            print(self.__persons[name].address())
        elif name not in self.__persons:
            print("address unknown")
            print("number unknown")
        else:
            for num in self.__persons[name].numbers():
                print(num)
            print(self.__persons[name].address())


    def add_address(self, name:str, address:str):
        if name in self.__persons:
            self.__persons[name].add_address(address)
        else:
            self.__persons[name] = Person(name = name, address=address)

    def all_entries(self):
        return self.__persons

class PhoneBookApplication:
    def __init__(self):
        self.__phonebook = PhoneBook()

    def help(self):
        print("commands: ")
        print("0 exit")
        print("1 add number")
        print("2 search")
        print("3 add address")

    def add_number(self):
        name = input("name: ")
        number = input("number: ")
        self.__phonebook.add_number(name, number)

    def search(self):
        name = input("name: ")
        self.__phonebook.get_entry(name)

    def add_address(self):
        name = input("name: ")
        address = input("address: ")
        if address:
            self.__phonebook.add_address(name, address)
        else:
            raise ValueError("Enter a valid address.")

    def execute(self):
        self.help()
        while True:
            print("")
            command = input("command: ")
            if command == "0":
                break
            elif command == "1":
                self.add_number()
            elif command == "2":
                self.search()
            elif command == "3":
                self.add_address()
            else:
                self.help()

class Person:
    def __init__(self, name:str, address:str = None, numbers:list = None):
        self.__name = name
        self.__address = address if address is not None else ""
        self.__numbers = numbers if numbers is not None else []

    def name(self):
        return self.__name

    def numbers(self):
        return self.__numbers

    def address(self):
        if self.__address:
            return self.__address
        else:
            return None

    def add_number(self, number:str):
        if number:
            self.__numbers.append(number)
        else:
            raise ValueError("Enter a valid number.")

    def add_address(self, address:str):
        if address:
            self.__address = address
        else:
            raise ValueError("Enter a valid address.")

    def __str__(self):
        return f"{self.numbers()}\n{self.address()}"


phonebook = PhoneBookApplication()
phonebook.execute()

TEST FAIL OUTPUT:

#IMPORTANT TEST FAIL OUTPUT:
# Test failed
# PhoneBook2_Part2Test: test_3_works_many_numbers
# The output of your program should be
# 040-999999
# with input
# 1
# Emilia
# 09-123456
# 1
# Emilia
# 040-999999
# 2
# Emilia
# 0
# Now the output was
# commands:
# 0 exit
# 1 add number
# 2 search
# 3 add address
#
# 09-123456
# address unknown

TEST ON WHICH CODE FAILS:

import unittest
from unittest.mock import patch

from tmc import points, reflect
from tmc.utils import load, load_module, reload_module, get_stdout, check_source
from functools import reduce
import os
import os.path
import textwrap
from random import choice, randint
from datetime import date, datetime, timedelta

def test_3_works_many_numbers(self):
    syote = ["1", "Emilia", "09-123456", "1", "Emilia", "040-999999", "2", "Emilia", "0"]
    with patch('builtins.input', side_effect=syote):
        try:
            reload_module(self.module)
        except:
            self.fail(f"Check that the program works with input\n{s(syote)}")

        output = get_stdout()
        expected = "09-123456"
        self.assertTrue(expected in output,
                        f"The output of your program should be\n{expected}\nwith input\n{s(syote)}\nNow the output was\n{output}")
        expected = "040-999999"
        self.assertTrue(expected in output,
                        f"The output of your program should be\n{expected}\nwith input\n{s(syote)}\nNow the output was\n{output}")
Share Improve this question edited Mar 15 at 20:44 Carcigenicate 45.9k11 gold badges77 silver badges129 bronze badges asked Mar 15 at 20:36 Miloš GrujićMiloš Grujić 193 bronze badges 1
  • 1 This is not a very helpful question for the community, so I think you should just check your code more thoroughly.. But if you are stuck, check that you print all the numbers that the person has, if the address is missing - I think the requirement is that and currently you are printing just numbers[0] which is just the first number. IF you get it right, I think you should consider deleting the question – Luihis Commented Mar 15 at 21:09
Add a comment  | 

1 Answer 1

Reset to default 0

name mangling

Please don't do this.

class PhoneBook:
    def __init__(self):
        self.__persons = {}

Much better to assign self._persons, with a single _ underscore.

Similarly for the Person attributes {name, address, numbers}.

use the "batteries included"

The add_number() method is tediously long. Prefer to rely on defaultdict(Person).

dataclass

The Person class really wants to be a @dataclass.

This aspect of the signature is obviously incorrect, as flagged by mypy:

address: str = None,

How do we know? Because a string is not None.

Similarly for numbers, as None is clearly not a list.

property

This call-site access, and the implementation, feel a bit off.

        if ... and not self.__persons[name].address():

        ...

    def address(self):
        if self.__address:
            return self.__address
        else:
            return None

I mean, sure, it works. But it wants to be an @property, without the () method call notation.

Similarly for a numbers property.

Also, it's unclear how returning None is somehow an improvement on returning the empty container [].

evaluate for side effects

    def get_entry(self, name: str):

Thank you for the nice annotation. But it doesn't go far enough; it should point out that we return ... -> None:

Which highlights that this is the Wrong Name. Do we "get" an entry and return it? No! We "print" an entry. Clearly this should be def print_entry or def display_entry, or something like that. Names really matter; they affect how we reason about code.

mapping

Using {if, elif, elif} is fine as far as it goes. You might prefer to use a dict for dispatching to a function:

    dispatch = {
        "0": sys.exit,
        "1": self.add_number,
        "2": self.search,
        "3": self.add_address,
    }

obtaining input param via stdin

This seems ill advised.

def test_3_works_many_numbers(self):
    syote = ["1", "Emilia", "09-123456", ... ]
    with patch('builtins.input', side_effect=syote):
        try:
            reload_module(self.module)

I have no idea what "syote" means. But it looks like you're trying to override sys.stdin to present certain inputs to the target code.

A more appropriate way to "instrument the target code!" with automated tests would be to refactor the function into a pair of functions, one of which accepts simple input parameters and the other which calls input(). Obviously consuming characters from stdin is a side effect, so one will be a "pure" function, while the other will have side effects. The upside for your code base is you will find that some kinds of functions are easier to test than others.

You might also consider defining a function which accepts input params from a file descriptor or a Path.

发布评论

评论列表(0)

  1. 暂无评论