Задача: 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?