title: Memoization of method working on python 3.6 but not on 3.7.3
body: <p>I use a decorator to extend memoization via lru_cache to methods of objects which aren't themselves hashable (following <a href="https://stackoverflow.com/questions/33672412/python-functools-lru-cache-with-class-methods-release-object">stackoverflow.com/questions/33672412/python-functools-lru-cache-with-class-methods-release-object</a>). <strong>This memoization works fine with python 3.6 but shows unexpected behavior on python 3.7.</strong> </p>
  
  <p><em>Observed behavior:</em>
  If the memoized method is called with keyword arguments, memoization works fine on both the python versions. If it's called without keyword arg syntax, it works on 3.6 but not on 3.7.</p>
  
  <p>==> What could cause the different behavior?</p>
  
  <p>The code sample below shows a minimal example which reproduces the behavior.</p>
  
  <p><code>test_memoization_kwarg_call</code> passes for both python 3.6 and 3.7.
  <code>test_memoization_arg_call</code> passes for python 3.6 but fails for 3.7.</p>
  
  <pre class="lang-py prettyprint-override"><code>import random
  import weakref
  from functools import lru_cache
  
  
  def memoize_method(func):
      # From stackoverflow.com/questions/33672412/python-functools-lru-cache-with-class-methods-release-object
      def wrapped_func(self, *args, **kwargs):
          self_weak = weakref.ref(self)
  
          @lru_cache()
          def cached_method(*args_, **kwargs_):
              return func(self_weak(), *args_, **kwargs_)
  
          setattr(self, func.__name__, cached_method)
          print(args)
          print(kwargs)
          return cached_method(*args, **kwargs)
  
      return wrapped_func
  
  
  class MyClass:
      @memoize_method
      def randint(self, param):
          return random.randint(0, int(1E9))
  
  
  def test_memoization_kwarg_call():
      obj = MyClass()
      assert obj.randint(param=1) == obj.randint(param=1)
      assert obj.randint(1) == obj.randint(1)
  
  
  def test_memoization_arg_call():
      obj = MyClass()
      assert obj.randint(1) == obj.randint(1)
  </code></pre>
  
  <p>Note that, weirdly, the line <code>assert obj.randint(1) == obj.randint(1)</code> does not lead to a test failure in <code>test_memoization_kwarg_call</code> when used in python 3.6 but fails for python 3.7 inside <code>test_memoization_arg_call</code>.</p>
  
  <p>Python versions: 3.6.8 and 3.7.3, respectively.</p>
  
  <h3>Further info</h3>
  
  <p>user2357112 suggested to inspect <code>import dis; dis.dis(test_memoization_arg_call)</code>. 
  On python 3.6 this gives</p>
  
  <pre><code> 36           0 LOAD_GLOBAL              0 (MyClass)
                2 CALL_FUNCTION            0
                4 STORE_FAST               0 (obj)
  
   37           6 LOAD_FAST                0 (obj)
                8 LOAD_ATTR                1 (randint)
               10 LOAD_CONST               1 (1)
               12 CALL_FUNCTION            1
               14 LOAD_FAST                0 (obj)
               16 LOAD_ATTR                1 (randint)
               18 LOAD_CONST               1 (1)
               20 CALL_FUNCTION            1
               22 COMPARE_OP               2 (==)
               24 POP_JUMP_IF_TRUE        30
               26 LOAD_GLOBAL              2 (AssertionError)
               28 RAISE_VARARGS            1
          &gt;&gt;   30 LOAD_CONST               0 (None)
               32 RETURN_VALUE
  </code></pre>
  
  <p>On python 3.7 this gives</p>
  
  <pre><code> 36           0 LOAD_GLOBAL              0 (MyClass)
                2 CALL_FUNCTION            0
                4 STORE_FAST               0 (obj)
  
   37           6 LOAD_FAST                0 (obj)
                8 LOAD_METHOD              1 (randint)
               10 LOAD_CONST               1 (1)
               12 CALL_METHOD              1
               14 LOAD_FAST                0 (obj)
               16 LOAD_METHOD              1 (randint)
               18 LOAD_CONST               1 (1)
               20 CALL_METHOD              1
               22 COMPARE_OP               2 (==)
               24 POP_JUMP_IF_TRUE        30
               26 LOAD_GLOBAL              2 (AssertionError)
               28 RAISE_VARARGS            1
          &gt;&gt;   30 LOAD_CONST               0 (None)
               32 RETURN_VALUE
  </code></pre>
  
  <p>the difference being that on 3.6 the call to the cached <code>randint</code> method yields <code>LOAD_ATTR, LOAD_CONST, CALL_FUNCTION</code> while on 3.7 it is yields <code>LOAD_METHOD, LOAD_CONST, CALL_METHOD</code>. This may explain the difference in behavior but I do not understand the internals of CPython (?) to understand it. Any ideas?</p>