timeit

Програмиране с Python

Курс във Факултета по Математика и Информатика към СУ

Решение на Аритметични изрази от Николай Лазаров

Обратно към всички решения

Към профила на Николай Лазаров

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 17 успешни тест(а)
  • 2 неуспешни тест(а)

Код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class Operator:
    def __init__(self, symbol, func):
        self.symbol = symbol
        self.func = func

    def __str__(self):
        return self.symbol

    def __call__(self, *argv, **kwargs):
        return self.func(*argv, **kwargs)

plus = Operator('+', lambda lhs, rhs: lhs + rhs)
minus = Operator('-', lambda lhs, rhs: lhs - rhs)
multiply = Operator('*', lambda lhs, rhs: lhs * rhs)
divide = Operator('/', lambda lhs, rhs: lhs / rhs)


def expressify(func):
    def inner(self, other):
        if isinstance(other, (int, float, complex)):
            other = Constant(other)

        return func(self, other)

    return inner


class BaseExpression:
    @expressify
    def __add__(self, other):
        return Expression((self, plus, other))

    @expressify
    def __sub__(self, other):
        return Expression((self, minus, other))

    @expressify
    def __mul__(self, other):
        return Expression((self, multiply, other))

    @expressify
    def __truediv__(self, other):
        return Expression((self, divide, other))

    @expressify
    def __radd__(self, other):
        return Expression((other, plus, self))

    @expressify
    def __rsub__(self, other):
        return Expression((other, minus, self))

    @expressify
    def __rmul__(self, other):
        return Expression((other, multiply, self))

    @expressify
    def __rtruediv__(self, other):
        return Expression((other, divide, self))


class Constant(BaseExpression):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return str(self.value)

    def evaluate(self, **variable_names):
        return self.value


class Variable(BaseExpression):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def evaluate(self, **variables):
        return variables[self.name]


class Expression(BaseExpression):
    def _add_variables(self, expr):
        if isinstance(expr, Variable):
            self.variable_names.add(expr.name)
        elif isinstance(expr, Expression):
            self.variable_names |= expr.variable_names

    def __init__(self, expression_structure):
        self.left = expression_structure[0]
        self.operator = expression_structure[1]
        self.right = expression_structure[2]

        if not isinstance(self.left, (Variable, Constant, Expression)):
            self.left = Expression(self.left)

        if not isinstance(self.right, (Variable, Constant, Expression)):
            self.right = Expression(expression_structure[2])

        self.variable_names = set()
        self._add_variables(self.left)
        self._add_variables(self.right)

    def __str__(self):
        return '({0} {1} {2})'.format(str(self.left),
                                      str(self.operator),
                                      str(self.right))

    def evaluate(self, **variables):
        return self.operator(self.left.evaluate(**variables),
                             self.right.evaluate(**variables))


def create_constant(value):
    return Constant(value)


def create_variable(name):
    return Variable(name)


def create_operator(symbol, function):
    return Operator(symbol, function)


def create_expression(expression_structure):
    return Expression(expression_structure)

Лог от изпълнението

.E..............E..
======================================================================
ERROR: test_create_expression_with_literal (test.TestArithmeticsWithLiterals)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data/rails/pyfmi-2016/releases/20160307095126/lib/language/python/runner.py", line 67, in thread
    raise result
TypeError: 'int' object is not subscriptable

======================================================================
ERROR: test_variables_and_constants_extra_operators (test.TestNativeOperators)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data/rails/pyfmi-2016/releases/20160307095126/lib/language/python/runner.py", line 67, in thread
    raise result
TypeError: unsupported operand type(s) for ** or pow(): 'Variable' and 'Variable'

----------------------------------------------------------------------
Ran 19 tests in 0.116s

FAILED (errors=2)

История (2 версии и 2 коментара)

Николай обнови решението на 17.03.2016 08:39 (преди почти 2 години)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class Operator:
    def __init__(self, symbol, func):
        self.symbol = symbol
        self.func = func

    def __str__(self):
        return self.symbol

    def __call__(self, *argv, **kwargs):
        return self.func(*argv, **kwargs)

Operator.plus = Operator('+', lambda lhs, rhs: lhs + rhs)
Operator.minus = Operator('-', lambda lhs, rhs: lhs - rhs)
Operator.multiply = Operator('*', lambda lhs, rhs: lhs * rhs)
Operator.divide = Operator('/', lambda lhs, rhs: lhs / rhs)


class AbstractExpression:
    # converts function parameters like this:
    # int, float, complex -> Constant
    def expressify(func):
        def inner(self, other):
            if type(other) in [int, float, complex]:
                other = Constant(other)

            return func(self, other)

        return inner

    @expressify
    def __add__(self, other):
        return Expression((self, Operator.plus, other))

    @expressify
    def __sub__(self, other):
        return Expression((self, Operator.minus, other))

    @expressify
    def __mul__(self, other):
        return Expression((self, Operator.multiply, other))

    @expressify
    def __truediv__(self, other):
        return Expression((self, Operator.divide, other))

    @expressify
    def __radd__(self, other):
        return Expression((other, Operator.plus, self))

    @expressify
    def __rsub__(self, other):
        return Expression((other, Operator.minus, self))

    @expressify
    def __rmul__(self, other):
        return Expression((other, Operator.multiply, self))

    @expressify
    def __rtruediv__(self, other):
        return Expression((other, Operator.divide, self))


class Constant(AbstractExpression):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return str(self.value)

    def evaluate(self, **variable_names):
        return self.value


class Variable(AbstractExpression):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def evaluate(self, **variables):
        return variables[self.name]


class Expression(AbstractExpression):
    def __init__(self, expression_structure):
        if type(expression_structure[0]) in [Variable, Constant, Expression]:
            self.left = expression_structure[0]
        else:
            self.left = Expression(expression_structure[0])

        self.operator = expression_structure[1]

        if type(expression_structure[2]) in [Variable, Constant, Expression]:
            self.right = expression_structure[2]
        else:
            self.right = Expression(expression_structure[2])

        self.variable_names = set()

        if type(self.left) is Variable:
            self.variable_names.add(self.left.name)
        elif type(self.left) is Expression:
            self.variable_names |= self.left.variable_names

        if type(self.right) is Variable:
            self.variable_names.add(self.right.name)
        elif type(self.right) is Expression:
            self.variable_names |= self.right.variable_names

    def __str__(self):
        lhs = str(self.left)
        op = str(self.operator)
        rhs = str(self.right)
        return '({0} {1} {2})'.format(lhs, op, rhs)

    def evaluate(self, **variables):
        lhs = self.left.evaluate(**variables)
        rhs = self.right.evaluate(**variables)
        return self.operator(lhs, rhs)


def create_constant(value):
    return Constant(value)


def create_variable(name):
    return Variable(name)


def create_operator(symbol, function):
    return Operator(symbol, function)


def create_expression(expression_structure):
    return Expression(expression_structure)
  • Не е добра идея да закачаш вече създадени инстанции като атрибути на класа. По този начин, всеки оператор си има оператори за събиране, изваждане, деление и умножение. Ок съм да ги ги оставиш като имена в глобалния scope.

  • Не мисля, че expressify има нужда от документиране, но ако наистина смяташ, че е нужно в Python документираме така.

  • Докато сме на темата с expressify, той е в тялото на клас, а изобщо не ми прилича на метод и не може да бъде използван като такъв. Изнеси го като функция в глобалния scope.

  • Не сравнявай резултата от type(...), а използвай isinstance, защото type(collections.defaultdict()) != dict, но isinstance(collections.defaultdict(), dict) връща истина, примерно.

  • AbstractExpression не е особено абстрактен. Можем ли да се съгласим, че е просто базов клас за изрази и може да бъде наречен BaseExpression?

  • Expression.__init__ е страшно сложен с тези if-else блокове.

  • В evaluate и __str__ на Expression дефинираш променливи, за да ги върнеш на следващия ред

Николай обнови решението на 17.03.2016 21:56 (преди почти 2 години)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class Operator:
    def __init__(self, symbol, func):
        self.symbol = symbol
        self.func = func

    def __str__(self):
        return self.symbol

    def __call__(self, *argv, **kwargs):
        return self.func(*argv, **kwargs)

plus = Operator('+', lambda lhs, rhs: lhs + rhs)
minus = Operator('-', lambda lhs, rhs: lhs - rhs)
multiply = Operator('*', lambda lhs, rhs: lhs * rhs)
divide = Operator('/', lambda lhs, rhs: lhs / rhs)


def expressify(func):
    def inner(self, other):
        if isinstance(other, (int, float, complex)):
            other = Constant(other)

        return func(self, other)

    return inner


class BaseExpression:
    @expressify
    def __add__(self, other):
        return Expression((self, plus, other))

    @expressify
    def __sub__(self, other):
        return Expression((self, minus, other))

    @expressify
    def __mul__(self, other):
        return Expression((self, multiply, other))

    @expressify
    def __truediv__(self, other):
        return Expression((self, divide, other))

    @expressify
    def __radd__(self, other):
        return Expression((other, plus, self))

    @expressify
    def __rsub__(self, other):
        return Expression((other, minus, self))

    @expressify
    def __rmul__(self, other):
        return Expression((other, multiply, self))

    @expressify
    def __rtruediv__(self, other):
        return Expression((other, divide, self))


class Constant(BaseExpression):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return str(self.value)

    def evaluate(self, **variable_names):
        return self.value


class Variable(BaseExpression):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def evaluate(self, **variables):
        return variables[self.name]


class Expression(BaseExpression):
    def _add_variables(self, expr):
        if isinstance(expr, Variable):
            self.variable_names.add(expr.name)
        elif isinstance(expr, Expression):
            self.variable_names |= expr.variable_names

    def __init__(self, expression_structure):
        self.left = expression_structure[0]
        self.operator = expression_structure[1]
        self.right = expression_structure[2]

        if not isinstance(self.left, (Variable, Constant, Expression)):
            self.left = Expression(self.left)

        if not isinstance(self.right, (Variable, Constant, Expression)):
            self.right = Expression(expression_structure[2])

        self.variable_names = set()
        self._add_variables(self.left)
        self._add_variables(self.right)

    def __str__(self):
        return '({0} {1} {2})'.format(str(self.left),
                                      str(self.operator),
                                      str(self.right))

    def evaluate(self, **variables):
        return self.operator(self.left.evaluate(**variables),
                             self.right.evaluate(**variables))


def create_constant(value):
    return Constant(value)


def create_variable(name):
    return Variable(name)


def create_operator(symbol, function):
    return Operator(symbol, function)


def create_expression(expression_structure):
    return Expression(expression_structure)

Не бих използвал името self извън контекста на клас, но освен това решението ти вече е далеч по-добре :)

edit: Можеш да опростиш if isinstance(other, (int, float, complex)) като използваш numbers.Number. Всички числа в python са негови наследници, именно поради тази причина.