Monday 26 June 2023

Shakshuka Hummus

I've recently spent a few days in Budapest. This has been my third time there and I intend to write a post about my impressions of the city (about architecture mainly) but this is going to be just a short post about food!

I like Hummus a lot. Furthermore we can say that sometimes when being abroad it's an essential part of my diet. In cities where I can not find particularly interesting and reasonably priced food in restaurants, going to the supermarket and buying an hummus box is an excellent option. I remember than when I was in Copenhagen I mainly survived on hummus. Apart from the different hummus types that you can find in supermarkets (classic, with paprika, with beetroot, with olives, spicy...) and the classic hummus that you find in the average Lebanese restaurant, there's much more to it.

When looking into what to eat before my first visit to Budapest in 2009 I had found an Hummus restaurant (indeed it was a chain of at that time 3 restaurants I think), proposing different varieties of hummus. I think it was advertised as "Israeli Hummus Restaurant" or something of that sort. I was there a couple of times I think (during that stay and in 2012), having classic hummus (with tehina and paprika). It was very good, but not something to keep coming in my dreams.

Before my recent trip I took a look to see if the restaurant still existed, and hopefully it does, and indeed has grown. Now it's a franchise that has expanded from Hungary and set shops in different countries, and now it has like 10 locations in Budapest. The menu has expanded and it's not just hummus (and falafel) and has influences from different oriental cuisines (unfortunately there are also some meat options, that I'm not sure they had in the past). The menu is almost the same in all shops, but with some differences. Some locations have a larger menu with some additional options, for example (vegetarian) couscous soup (I tried it in one of them and was pretty good). The menu had some really appelaling options, and I ended up having lunch and/or dinner there most days. I tried the hummus with mushrooms and the hummus with aubergines, both of them very good, but the outstanding option is the shakshuka hummus. This is what the menu has to say about Shakshuka

The legendary tomato and pepper gravy, cooked with sunny eggs, served with parsley and warm pita

And yes, it's really epic, absolutely delicious. The mix of hummus with that so tasty tomato sauce is magic. This has brought me memories of a place in Stockholm Reggev hummus. When I was in Stockholm I went there everyday for dinner, to have a delicious hummus with aubergines, paprika and tomato sauce. The owners of the restaurant were Israeli, so it seems like that country has something special with hummus. Buff, I'm salivating now.

Tuesday 20 June 2023

Python ABC's Implementation

In Python we normally define abstract classes by inheriting from abc.ABC, but the abstract classes logic really lives in the abc.ABCMeta metaclass. Given that the abc.ABC class has abc.ABCMeta as its metaclass, classes that inherit from abc.ABC also have abc.ABCMeta as its metaclass. This means that we can declare an abstract class by explicitly setting its metaclass MyAbstractClass(metaclass=abc.ABCMeta) (and indeed I think this was the initial way to declare abstract classes) or just by inheriting from the abc.ABC class (I guess this was introduced so that people would not even need to think about metaclasses).

PEP-3119 gives this interesting explanation of how abstract classes are implemented.

Implementation: The @abstractmethod decorator sets the function attribute __isabstractmethod__ to the value True. The ABCMeta.__new__ method computes the type attribute __abstractmethods__ as the set of all method names that have an __isabstractmethod__ attribute whose value is true. It does this by combining the __abstractmethods__ attributes of the base classes, adding the names of all methods in the new class dict that have a true __isabstractmethod__ attribute, and removing the names of all methods in the new class dict that don’t have a true __isabstractmethod__ attribute. If the resulting __abstractmethods__ set is non-empty, the class is considered abstract, and attempts to instantiate it will raise TypeError.

So this is a very interesting use case of metaclasses. As explained in the above paragraph, when we define an abstract class we are creating an instance of ABCMeta, so the __new__ method of ABCMeta is invoked. I was thinking that further leveraging metaclasses, ABCMeta would also have a __call__ method so that when you try to create an instance of an abstract class that __call__ method would get invoked, and it would perform that check of the __abstractmethods__ attribute, but I see in the source code of ABCMeta that such __call__ method does not exist. Quite intrigued by this, I downloaded cPython source code and searching for the "Can't instantiate abstract class" error message I found that this check is directly done in typeobject.c, in the function that I think creates an object.

So the __abstractmethods__ attribute is calculated when the class is defined. This is important because it does not play so good with the dynamic features of Python. Let's say we create a class that inherits from and abstract class an in which we do not implement one of the inherited abstract methods, so the class is "marked" as abstract. If then we dynamically add that method to the class, the __abstractmethods__ are not recalculated, and the class continues to be considered as abstract and not instantiable (we get the "Exception: Can't instantiate abstract class" error if we try to instantiate it). Let's see an example:


from abc import ABC, abstractmethod, update_abstractmethods

class Widget(ABC):
    @abstractmethod
    def draw(self):
        pass

    @abstractmethod
    def move(self):
        pass

class MovableItem:
    def move(self):
        print(f"I'm moving")

class Button(Widget):
    def draw(self):
        print(f"I'm drawing myself")

Button.move = MovableItem.move

try:
    # this instantiation fails
    b1 = Button()
except Exception as ex:
    print(f"Exception: {ex}")
#Exception: Can't instantiate abstract class Button with abstract method move


Hopefully we can ask the "abstract status" to be recalculated, by using update_abstractmethods


update_abstractmethods(Button)

try:
    # works fine
    b1 = Button()
    b1.move()
except Exception as ex:
    print(f"Exception: {ex}")

There's an additional issue that can not be fixed with update_abstractmethods. If I define a class that inherits from an abstract class and gets the implementation of one of the abstract methods from another parent class, such method is not considered as implemented, and it shows up in the __abstractmethods__ of the class, making it uninstantiable. This behaviour seems a bit odd to me, and I've verified that for example in Kotlin if one class implements an interface and extends a class, methods in the parent class can provide the implementation of the interface without a problem. If you read carefully the explanation in that paragraph above about how abstract classes are implemented, this behaviour makes sense, but as I've said it's not what I would expect.

abstract/inheritance1.py

from abc import ABC, abstractmethod, update_abstractmethods

class Widget(ABC):
    @abstractmethod
    def draw(self):
        pass

    @abstractmethod
    def move(self):
        pass


class MovableItem:
    def move(self):
        print(f"I'm moving")


class Button(Widget, MovableItem):
    def draw(self):
        print(f"I'm drawing myself")

try:
    b1 = Button()
    # though I'm inheriting the move method from MovableItem, the abstract one in Widget remains and I get an exception creating the object
except Exception as ex:
    print(f"Exception: {ex}")
# Exception: Can't instantiate abstract class Button with abstract method move

You can fix it by defining the problematic method in the derived class and invoking from it the implementation in the parent class.


class Button2(Widget, MovableItem):
    def draw(self):
        print(f"I'm drawing myself")
    
    def move(self):
        # if we use super the resolution ends up in the move of the abstract, Widget class, that does nothing
        #super().move()
        MovableItem.move(self)


b2 = Button2()
b2.draw()
b2.move()
# I'm drawing myself
# I'm moving

By the way, there's an interesting discussion here about the best way to check if a class is abstract.

Tuesday 6 June 2023

Python Metaclasses part 2

Last year I wrote a post about the mysterious world of Python Metaclasses. In that post I just mentioned in passing that the interaction of inheritance and metaclasses adds a bit more of complexity. I've been revisiting this topic and I've thought of writing short complementary post.

One class has just one single metaclass. If that metaclass has not been declared explicitly, e.g. MyClass(metaclass=MyMeta): the metaclass will be taken from the inheritance chain. As most classes do not declare a metaclass, most times the search in the inheritance tree ends in its root, object, that has type as metaclass. The problem with this is that Python has Multiple Inheritance, so what if we have explicit, unrelated metaclasses declared in different branches of the multiple inheritance tree of our class? Well, in that case we get an error:
Exception: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Let's see an example:


# declare 2 metaclasses, MetaA and MetaB
class MetaA(type):
    def __new__(mcls, name, bases, attrs):
        print(f"{mcls.__name__} {name} - MetaA")
        return super().__new__(mcls, name, bases, attrs)


class MetaB(type):
    def __new__(mcls, name, bases, attrs):
        print(f"{mcls.__name__} {name} - MetaB")
        return super().__new__(mcls, name, bases, attrs)



class A(metaclass=MetaA):
    pass

# MetaA A - MetaA


class B(metaclass=MetaB):
    pass

# MetaB B - MetaB


# try to create a class that inherits from classes that have different, unrelated metaclasses
try:
    class AB(A, B):
        pass
except Exception as ex:
    print(f"Exception: {ex}")
    # MetaA -> type <- MetaB
    
# Exception: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases



If the metaclasses that we obtain from the different branches in the inheritance tree are all in the same inheritance "line" that's fine, we'll get as metaclass the most derived one.


# create another metaclass (it inherits from one of the previous metaclasses)
class MetaAA(MetaA):
    def __new__(mcls, name, bases, attrs):
        print(f"{mcls.__name__} {name} - MetaAA")
        return super().__new__(mcls, name, bases, attrs)


class AA(metaclass=MetaAA):
    pass

# MetaAA AA - MetaAA
# MetaAA AA - MetaA

print(f"type(AA): {type(AA).__name__}")
# type(AA): MetaAA


# inherit from 2 classes that have different metaclasses, but both metaclasses are in the same inheritance "line"
class AAA(AA, A):
    pass

# MetaAA AAA - MetaAA
# MetaAA AAA - MetaA

# it's fine cause AA.metaclass and A.metaclass are in the same inheritance chain
# MetaAA -> MetaA -> type
# it gets as metaclass the most derived one, MetaAA

print(f"type(AAA): {type(AAA).__name__}")
# type(AAA): MetaAA


So long in short, for the metaclasses from the inheritance tree this is fine:
MetaA -> MetaB -> type
but this is not:
MetaA -> type <- MetaB

Notice that one metaclass can inherit from 2 metaclasses. So if you set that derived metaclass as your class metaclass, it's sort of a way of "almost" having 2 metaclasses.



# on the other hand, a metaclass can inherit from several metaclasses
class MetaAB(MetaA, MetaB):
    def __new__(mcls, name, bases, attrs):
        print(f"{mcls.__name__} {name} - MetaAB")
        return super().__new__(mcls, name, bases, attrs)
    
class AB(metaclass=MetaAB):
    pass

# MetaAB AB - MetaAB
# MetaAB AB - MetaA
# MetaAB AB - MetaB


Saturday 3 June 2023

Destructuring and Custom Unpacking

Years ago I talked about Destructuring Assignment in JavaScript and C#. We could say that this feature in Python is quite similar to its JavaScript counterpart, and in Kotlin it's quite similar to the C# one. While in JavaScript and Python we use destructuring mainly with Arrays or lists/tuples, it can be used with any iterable object. I'm not sure why Kotlin did not follow this same iterable logic. To be "destructurable" Kotlin objects have to provide the component1(), component2()... methods, which is not so different from C# approach and deconstructors.

Reading this discussion about destructuring in Kotlin I've found what seems a good nomenclature to distinguish 2 types of destructuring: Positional and Nominal (featured only by JavaScript).

Positional Destructuring Assignment means that values are assigned to the variables just based on their position. The first variable gets the first value returned by the iterable (or by component1()) and so on. Nominal Destructuring Assignment is the JavaScript feature where we can destructure an object by assigning to a variable the property named as that variable, I mean:


const user = {
  id: 42,
  isVerified: true,
};

const { id, isVerified } = user;

console.log(id); // 42
console.log(isVerified); // true

One different feature that I tend to relate to destructuring are the rest-spread operator in JavaScript ("...") and the pack-unpack operators ("*", "**") in Python. The relation is that we can use rest-pack when destructuring, like this:


> function* cities() {
... yield "Paris";
... yield "Xixon";
... yield "Uvieu";
}

> let [a, ...b] = cities();

> a;
'Paris'
> b;
[ 'Xixon', 'Uvieu' ]



def cities():
    yield "Paris"
    yield "Xixon"
    yield "Uvieu"
    
a, *b = cities()

a
# 'Paris'
b
# ['Xixon', 'Uvieu']

In JavaScript, same as we can use Positional and Nominal destructuring, we can also use rest-spread not only with iterables, but also with any object. In Python, we can use "*" with any iterable object and "**" with dictionaries. Well, this second point is interesting, cause I've learned here that "**" works with any object that implements the collections.abc.Mapping abstract class. We have to explicitly extend collections.abc.Mapping, just adding those methods to our class and expecting Duck Typing or runtime Structural Typing to work won't make it.


from collections.abc import Mapping
# Structural typing won't work for this:
#class Person:

#I have to explicitly declare that it implements Mapping
class Person(Mapping):    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.stress_level = 1

    def __getitem__(self, key):
        return getattr(self, key)

    def __iter__(self):
        yield 'name'
        yield 'age'

    def __len__(self):
        return 2

class Politician:
    def __init__(self, name, age, party):
        self.name = name
        self.age = age
        self.party = party

p1 = Person("Xuan", 47)
politician = Politician(**p1, party="Radical Center")

print(politician.__dict__)

--------------