Трето домашно

  1. Здравейте, въпрос: какво трябва да прави нашия клас, ако се ползва по следния начин:

    person = BiDict({'name': 'Кънчо', 'age': 18, 'sex': 'M'})
    person['age'] = 'M'

    Това очевидно не е валидно, но все пак от невалидно до невалидно има много разстояние. Та какво точно трябва да направи BiDict в такава ситуация?

  2. Всъщност си е напълно валидно. Както задаването на нов ключ в dict премахва текущата двойка с такъв ключ, така задаването на нова стойност се предполага да премахва текущата двойка с подобна стойност:

    >>> person = BiDict({'name': 'Кънчо', 'age': 18, 'sex': 'M'})
    >>> person['age'] = 'M'
    >>> person
    BiDict({'age': 'M', 'name': 'Кънчо'})
    

    П.П.: Благодаря ти, че се сети за това, тъй като не сметнах за нужно да добавям това в примерните тестове.

    Което ме подсеща - ако предоставяте ваши тестове, покриващи липсващи в примерните тестове случаи, ще бъдете поощрявани. Този case е добър пример за това.

  3. Когато нашият речник е обърнат трябва ли да помни кое оригинално са били ключове и кое стойности ? Предполагам, че това би трябвало да е поведението:

    >>> person['eyes'] = 'green'
    >>> person.inverse()
    >>> person['eyes'] = 'green'
    >>> person
    {'eyes': 'green', 'green': 'eyes'} 


    ,но не бих отказал потвърждение

  4. Здравейте, мисля, че въпреки поправката, в unittest-а за BiDict все още има поне 1 грешка:
    self.person.iverse()
    Другото, което ме притеснява, е дали в последният тест assert-а не трябва да е различен от това:
    self.assertIn('keys', self.person)
    Защото в момента, в който замених self.person с истински dict обект, тестът все още не минаваше (което ме кара да мисля, че проблемът не е в моя BiDict):

     ======================================================================
     FAIL: test_has_dict_attrs (__main__.BiDictTestCase)
     ----------------------------------------------------------------------
     Traceback (most recent call last):
       File "sample_test2.py", line 19, in test_has_dict_attrs
         self.assertIn('keys', self.person)
     AssertionError: 'keys' not found in {'age': 18, 'name': 'Кънчо', 'sex': 'M'}     
    

    Също така, тестът test_invalid_value мисля, че трябва да бъде така:
    self.assertRaises(TypeError, self.person.update, {'sports': ['boxing',]})
    вместо така
    self.assertRaises(TypeError, self.person.update({'sports': ['boxing',]}))

    Като източник за разликата давам този линк
    http://www.electricmonk.nl/log/2012/01/14/python-unittest-assertraises-pitfall/

  5. @Светослав: Можеш ли да ми обясниш как нещо подобно:

    def inverse(self, *args, **kwargs):
        self.showingOriginalKeys = not self.showingOriginalKeys
        ... #inversing here
    

    би ти удвоило(надявам се нямаш предвид 'дублира', по очевидни причини) записите в BiDict-а след две обръщания ?

  6. Въпрос инспириран от вече предадените решения: Тъй като от BiDict се очаква да се държи като dict, добра идея ли е нашият тип да наследява dict?

    Питам, тъй като във вече предадените решения и от двата вида. Някой, с достатъчно добра аргументация?

  7. Композицията сякаш е по-мощна.

    Вярно ще трябва да се напишат всички методи от интерфейса на dict(за да се държи като него) вместо да разчитаме, че при нужда ще се намерят в родителския клас, но така имаме пълен контрол и свобода над вътрешната имплементация на нашия нов клас. И при необходимост можем да я променяме без да влияем на интерфейса и съответно да чупим код, който вече използва нашия клас.

  8. Новият клас е с изключително близка функционалност до тази на dict (дори е инспирирана от него). До толкова, че е необходима имплементацията на два метода за да работи както се очаква. В друг случай може би е по-добре да имаш контрол върху всички методи, но мисля че това е перфектна ситуация за имплементация на наследяване и показване на положителните му страни. С други думи, ако сега не се ползва наследяване - кога?

  9. Поиграх си малко с миксин, но честно казано не виждам особена разлика спрямо директно едномерно наследяване. Даже някои неща са малко поусложнени. Не съм сигурен дали не съм пропуснал нещо съществено при тях, но всичко, което трябва да се напише в директен наследник се пише в базов клас, който ще ползваме по-късно при множествено наследяване на BiDict. Като цяло няма лошо, ако не трябваше да си пишем единия родител(не съм търсил обстойно, но съм сравнително сигурен, че го няма готов някъде) или имаше смисъл от преизползването му, което ми се струва малко вероятно.

    Пропускам ли нещо съществено по миксините, защото наистина не виждам полза от тях тук?

  10. Въпрос свързан до някъде с първия въпрос на Явор Папазов. Ако при самото инициализиране имаме повтарящи се стойности за различните ключове какво трябва да се случи? Ето пример:

    person = BiDict({'name': 'M', 'age': 'M', 'sex': 'M'})

    По логиката горе би трябвало да остане само един от всичките записи, но кой? Или ще е по-добре да хвърляме някакъв exception?

  11. " Знаем, че ключовете във всеки речник трябва да бъдат уникални и непроменящи се(immutable). От Вас искаме да създадете нов тип, наречен BiDict, в който същите условия за ключовете да бъдат задължителни и за стойностите."

    Т.е. виж какво се случва като ключовете са повтарящи се, и направи същото и за стойностите.

  12. @Николай Георгиев: За себе си аз реших как да си пиша решението по следния начин: dict([(1, 2), (1, 3)]) връща {1: 3}, значи последният печели ;) Под последния имам предвид последния по итератор.

    Относно това дали трябва да наследява dict, аз бих дал теоретически аргумент: това е вариация на Circle-ellipse проблема: когато в dict напъхаме 10 двойки с различни ключове, този dict ще има 10 елемента, а за BiDict това не е задължително да е вярно. Според мен не трябва да се наследява от dict, по "философски" причини, а и освен това аз лично не виждам как inverse() може да се имплементира с О(1) сложност, ако има наследяване.

  13. Не ми звучи добре просто да игнорираш някое :/ иначе на мен ми се искаше да направя нещо г/д подобно с повтарящи се ключове и стойности (да запазвам и обръщам всичко :) ), ама за съжаление като препрочетох условието видях, че не дават :D

  14. @Явор Папазов: Благодаря ти за отговара. Аз в крайна сметка реших да хвърлям exception :]

    А по първият ти въпрос не видях добавен тест затова едно просто тестче от мен: def test_duplicate_values(self): self.person['name'] = 18 self.assertNotIn('age', self.person.keys())

  15. @Кирил Владимиров: По принцип тестовете, които сте предоставили ми минават без грешка. Ще го направя както казваш все пак :) Иначе ако някъде има други тестове казвайте. Аз ползвам само тези от git-a + моите.

  16. Имам въпрос за поведението на __eq__ за нашия речник. Само текущото състояние на речника ли ни интересува по време на сравнението?

    Какво очакваме в следната ситуация?

    >>> person = BiDict({'age': 'N', 'name': 'M'})
    >>> inversed = BiDict({'N': age, 'M': 'name'})
    >>> person == inversed # True or False?
    

Трябва да сте влезли в системата, за да може да отговаряте на теми.