1 Востаннє редагувалося koala (10.09.2021 09:46:40)

Тема: Як обманути оператор is?

Задача: https://www.codewars.com/kata/60c47b07f24d000019f722a2

англ.

In Python, == tests for equality and is tests for identity. Two strings that are equal may also be identical, but this is not guaranteed, but rather an implementation detail. Given a computed expression, ensure that it is identical to the corresponding equal string literal by completing the make_identical function. See test case for clarity. (Note, this is easy if you know the concept.)

Треба написати функцію make_identical, яка б приймала стрічку і проходила ось таку перевірку:

test.assert_equals("abc" is make_identical("abcd"[:-1]), True)

причому не лише з літералами.

Проблема в тому, що оператор is не перевизначається і перевіряє, по факту, адреси об'єктів - аргументів. Тобто якщо об'єкти хоч у якомусь сенсі різні, то й результат буде різний.

Спроба №1:

def make_identical(*args):
    return "abc"

Цей тест проходить, бо літеральні константи об'єднуються, але не далі.

Спроба №2:

import inspect
def make_identical(strng):    
    return inspect.currentframe().f_back.f_code.co_consts[1]

Виходимо з того, що на рівень вище за виклик у константах буде наш літерал. Знову ж, перший тест проходимо, далі - ні. Хоча це дивно для задачі рівня 7 кю - це лише трохи складніше за основи.

Наступною думкою було спробувати дизасемблювати те, що відбувається рівнем вище... і отримав ось таке:

import inspect
import dis

def make_identical(strng):    
    frame = inspect.currentframe().f_back
    print(dis.dis(frame.f_code))

це явно не рівень 7 кю (і навряд чи 6 кю) - і маю:

багатокоду
 16           0 LOAD_CONST               1 (<code object _the_random_test_solution at 0x7fb07f61d2f0, file "tests.py", line 16>)
              2 LOAD_CONST               2 ('_.<locals>.random_tests.<locals>._the_random_test_solution')
              4 MAKE_FUNCTION            0
              6 STORE_FAST               0 (_the_random_test_solution)

 19           8 LOAD_GLOBAL              0 (range)
             10 LOAD_CONST               3 (30)
             12 CALL_FUNCTION            1
             14 GET_ITER
        >>   16 FOR_ITER               140 (to 158)
             18 STORE_FAST               1 (_)

 20          20 LOAD_GLOBAL              1 (random)
             22 LOAD_METHOD              2 (randrange)
             24 LOAD_CONST               4 (10)
             26 CALL_METHOD              1
             28 STORE_FAST               2 (n)

 21          30 LOAD_GLOBAL              1 (random)
             32 LOAD_METHOD              2 (randrange)
             34 LOAD_CONST               4 (10)
             36 CALL_METHOD              1
             38 STORE_FAST               3 (k)

 22          40 LOAD_GLOBAL              1 (random)
             42 LOAD_METHOD              3 (sample)
             44 LOAD_GLOBAL              0 (range)
             46 LOAD_CONST               3 (30)
             48 CALL_FUNCTION            1
             50 LOAD_FAST                3 (k)
             52 CALL_METHOD              2
             54 STORE_FAST               4 (s)

 23          56 LOAD_CONST               5 ('')
             58 LOAD_METHOD              4 (join)
             60 LOAD_GLOBAL              5 (map)
             62 LOAD_GLOBAL              6 (str)
             64 LOAD_FAST                4 (s)
             66 CALL_FUNCTION            2
             68 CALL_METHOD              1
             70 STORE_FAST               4 (s)

 24          72 LOAD_GLOBAL              7 (test)
             74 LOAD_METHOD              8 (assert_equals)
             76 LOAD_GLOBAL              9 (make_identical)
             78 LOAD_FAST                4 (s)
             80 LOAD_CONST               0 (None)
             82 LOAD_FAST                2 (n)
             84 BUILD_SLICE              2
             86 BINARY_SUBSCR
             88 CALL_FUNCTION            1
             90 LOAD_FAST                0 (_the_random_test_solution)
             92 LOAD_FAST                4 (s)
             94 LOAD_CONST               0 (None)
             96 LOAD_FAST                2 (n)
             98 BUILD_SLICE              2
            100 BINARY_SUBSCR
            102 CALL_FUNCTION            1
            104 COMPARE_OP               8 (is)
            106 LOAD_CONST               6 (True)
            108 CALL_METHOD              2
            110 POP_TOP

 25         112 LOAD_GLOBAL              7 (test)
            114 LOAD_METHOD              8 (assert_equals)
            116 LOAD_GLOBAL              9 (make_identical)
            118 LOAD_FAST                4 (s)
            120 LOAD_CONST               0 (None)
            122 LOAD_FAST                2 (n)
            124 BUILD_SLICE              2
            126 BINARY_SUBSCR
            128 CALL_FUNCTION            1
            130 LOAD_GLOBAL              9 (make_identical)
            132 LOAD_FAST                4 (s)
            134 LOAD_CONST               0 (None)
            136 LOAD_FAST                2 (n)
            138 BUILD_SLICE              2
            140 BINARY_SUBSCR
            142 LOAD_CONST               7 ('a')
            144 BINARY_ADD
            146 CALL_FUNCTION            1
            148 COMPARE_OP               9 (is not)
            150 LOAD_CONST               6 (True)
            152 CALL_METHOD              2
            154 POP_TOP
            156 JUMP_ABSOLUTE           16
        >>  158 LOAD_CONST               0 (None)
            160 RETURN_VALUE

#Гм. А я трохи неправильно це прочитав. Продовжу пізніше.
Тести генеруються випадково, треба звідкілясь ті змінні витягати.

Спроба №3:

import inspect
import itertools

def make_identical(strng):    
    frame = inspect.currentframe().f_back
    for x in itertools.chain(frame.f_locals.values(), frame.f_code.co_consts):
        if x == strng:
            return x

Фейл для більшості тестів. І це явно не рівень 7 кю. Я щось пропускаю, але що? Як можна обманути is?

Подякували: tchort, leofun012

2 Востаннє редагувалося mamkin haker (10.09.2021 09:50:18)

Re: Як обманути оператор is?

8 кю - знання основ, а 7 кю це трішки вище?
а 1-2 кю я так розумію це рівень Senior?

прийду з лікарні теж спробую цю задачку :D

3

Re: Як обманути оператор is?

Це змагальне програмування, тут трохи інші вимоги. Senior - то радше про архітектуру великих кавалків коду.
Типова задача 8 кю: виправити помилку в коді або написати простенький регулярний вираз.
Типова задача 5 кю: перекладач у код Морзе і назад
Типова задача 2 кю: декодер пошкодженого коду Морзе
Типова задача 1 кю: компілятор

4

Re: Як обманути оператор is?

задача в тому щоб в полі STDERR не було ось цого?

tests.py:8: SyntaxWarning: "is" with a literal. Did you mean "=="?
  test.assert_equals("abc" is make_identical("abcd"[:-1]), True)

5 Востаннє редагувалося koala (10.09.2021 12:14:45)

Re: Як обманути оператор is?

Ні, це якраз нормально. В останніх пітонах почали піклуватися про юзерів і повідомляти про очевидну дурню. А тут сенс якраз у тому, щоб ця очевидна дурня спрацьовувала.
Задача в тому, щоб пройти всі тести.

6

Re: Як обманути оператор is?

result:

Passed: 43
Failed: 18

def make_identical(strng):    
    return f"{strng}"

7 Востаннє редагувалося mamkin haker (10.09.2021 14:11:40)

Re: Як обманути оператор is?

54 - true
7 = false

import inspect
import itertools

def make_identical(strng):
    frame = inspect.currentframe().f_back
    for x in itertools.chain(frame.f_locals.values(), frame.f_code.co_consts):
        if x == strng:
            print(x, strng)
            return x
    return f"{strng}"

8

Re: Як обманути оператор is?

copy.copy(strng)

дає навіть

Passed: 48
Failed: 13

Але все одно не те.

9

Re: Як обманути оператор is?

Ні, тут треба щось знати про Python, чого ми не знаємо.

10 Востаннє редагувалося wander (10.09.2021 15:21:21)

Re: Як обманути оператор is?

import sys
def make_identical(strng):
    return sys.intern(strng)

??
https://docs.python.org/3/library/sys.html#sys.intern

Подякували: koala, dot, leofun013

11

Re: Як обманути оператор is?

from sys import intern as make_identical

Ну от і дізнався щось нове. Дякую.

12

Re: Як обманути оператор is?

Там ще є відповідь

def make_identical(strng):
    return eval(f'"{strng}"')

із купою варіацій. eval теж використовує intern.

13

Re: Як обманути оператор is?

Я в пітоні не сильний, але про internal таблиці знав, тому це друге, що спало в голову загуглити :)

Подякували: koala1

14 Востаннє редагувалося mamkin haker (10.09.2021 15:37:25)

Re: Як обманути оператор is?

ліл
все настільки просто?
(т_т)