Strange call stack

Hi, Im working on the TomeRater project for the Programming with Pyton class - you can find my entire code here:
https://repl.it/@bennypr0fane/Tome-Rater

I don’t understand

  1. why the self.users.get() call comes up empty and
  2. why the __eq__ method is called. Any clues?
  3. adding the book and the user works, the user ends up in the tomerater.users list, and the .get() method invoked in add_book_to_user works as well.
  4. However, the check: line 126, in add_book_to_user if self.users.get(email) == None: seems to come up emtpy!
  5. Inexplicably, User’s __eq__ method is called, and doesn’t like the fact that it’s being passed None
    But… there’s no call to __eq__ in add_book_to_user ! And the User object isn’t None !

Look at line 126:

 if self.users.get(email) == None:

The object returned by users.get() is a member of the Users class. And that class has a ‘dunder’ method __eq__() , created so that you can call it using the == symbol. Its purpose is to test if two users are the same, by comparing their names and email addresses…

So, when it sees the construction User.obj == something_else, the interpreter thinks that you are calling that method, to ask, “Is the User returned by self.users.get(email) the same user as the User called None?”, and it duly tries to compare name and email, thus throwing the error:

AttributeError: ‘NoneType’ object has no attribute ‘name’

1 Like

well then maybe I should chack against the key (email address) rather than the user object…?

I think that
if not self.users.get(email):
… would do what you want.

Total segue:

Along the lines of methodology, one can hardly escape the acid test of symbol based algorithms.

def kbv(d, v):
  '''
  d => a dictionary
  v => the value to match
  k => the list of keys for matched values
  a, b => unpackage of next item
  '''
  k = []
  for a, b in d.items():
    if b == v:
      k.append(a)
  return k

This is the stripped bare representation of the function. Clearly, it self documents itself once the algorithm is studied and known. Verbose object names are useful to a point, after which they are a hindrance. Negotiate with the verbose at one end of the transaction, and symbolize at the other. Makes for code that reads like a story.


It gets even better, but wrapping one’s head around it at present may not be easy. No sweat. It will come up, so this is a sneak peek…

def kbv(d, v):
  return [a for a, b in d.items() if b == v]
1 Like

@patrickd314, that worked, thank you!
Btw, is there a difference between
if email not in self.users:
and
if not email in self.users: ?

Thanks for the advice! The way I understand it, verbose names are supposed to help with reference. In natural language, this is achieved with pronouns (among others), which are like arrows pointing at a thing that was previously mentioned, or is just known to be in the world.
Here, (almost) all we’re left with is arbitrary names.

Sounds fun, but which goes on which end? Also, you need to have consistent object names throughout the code, right?

Objects are named at the caller, which to my mind is enough reference. Yes, function names should be verbose enough to define their meaning and purpose, and it makes sense to give meaningful names to objects at the calling and receiving end. We don’t need that level of verbosity in utility functions, imho. The above, ‘key_by_value’ is an example of a utility. We know the name of the object at the caller by the argument, and the name of the receiving object. Arbitrary symbols in the function make it clear what the function is doing without cluttering.

a is for apple

Once we know what an apple is, it is simple to visualize what the ‘a’ stands for in a confined space, such as a utility function. The function is completely closed off, and sharable by the entire program. A symbol is more generic than arbitrary when we think of it. Any dictionary, any value. The doc_string supplies enough information the way I see it.

1 Like
if not A in B

vs

if A not in B

They are quite different. NOT acts upon the operand immediately following, so,

NOT A

is the expression to be evaluated. Assuming A is some non-zero quantity, or a non-empty string, the value will be False.

if False in B

Doesn’t look quite right, does it?

if A not in B

now the expression is readable left to right.

To use the first expression we need grouping.

if not (A in B)

If A is a member of B, then the bracketed expression will yield, True, which not will toggle to False, giving the same result as,

if A not in B
1 Like

I think that’s the important point I was missing. Can’t seem to remember which operator takes precedence, gotta check that out again.

1 Like
  • brackets
  • not
  • and
  • or

is the precedent ordering of these operators, and comparison operators precede them.

1 Like