Saturday 30 April 2022

Python Bound Methods Part - 2

This post is a follow-up to this one. There I talked about how invoking a method through an instance of a class will search for that method in the class dictionary and return a bound method, that is, the method with the instance object bound to it, so that it will be passed as first parameter (what we usually call "self"). We'll see now some related details.

In python we have 2 options when declaring a method that does not use an instance of the class (has no "self), what in most languages we call static or class methods:
the @staticmethod decorator and the @classmethod decorator.
In both cases the method can be invoked through the class itself or an instance of that class, I mean MyClass.method() or my_instance.method().

The difference comes from whether the method needs to invoke other methods (classmethods or staticmethods) in the class. In the first case, you have to declare the method as a classmethod, so that it will receive as first parameter the class it belongs to. In the second case you declare it as staticmethod, and you will only receive the parameters that you have passed in the invocation.

My understanding of how classmethods invocation happens is like this. When invoking it through an instance, the lookup mechanism will search the method first (to not avail) in the instance.__dict__ , and then will search it (and find it) in the class.__dict__. Obviously, when invoked through the class, it'll directly search in the class.__dict__. Then, as the method has been decorated with @classmethod, a bound method object will be created (bound to the class) and returned (something like: bound method Person.sayHi of class '__main__.Person'). So when a method is retrieved from a class dictionary, if it's a @classmethod we get it bound to the class, if it's a @staticmethod we get it unbound, and if it's an instance method we get it bound to that instance if accessed through an instance, or unbound if accessed through the class. I mean:


class Person:
    PLANET = "Earth"
    def __init__(self, name):
        self.name = name
    
    def say_hi(self):
        print(f"Person {self.name} says hi")

    @classmethod
    def cls_meth(cls):
        print(cls.PLANET)

    @staticmethod
    def static_meth(msg):
        print(f"msg: {msg}")


p1 = Person("Francois")
print(Person.cls_meth) # bound method Person.cls_meth of class '__main__.Person'
print(p1.cls_meth) # bound method Person.cls_meth of class '__main__.Person'

print(Person.static_meth) # function Person.static_meth at 0x7f2bdddfd040
print((p1.static_meth)) # function Person.static_meth at 0x7f2bdddfd040

print(Person.say_hi) # function Person.say_hi at 0x7f2bdddf4ee0
print(p1.say_hi) # bound method Person.say_hi of __main__.Person object at 0x7f2bdde97730

As I've said, if we retrieve an instance method through the class rather than through an instance we get an unbound method. That unbound method is expecting to receive a "self", so we'll have to pass it along with the other parameters. We could pass an instance of a different class in that "self" (and thanks to duck typing if it has the properties expected by that method it will work).


class Book:
    def __init__(self, name):
        self.name = name

b1 = Book("Guerilla")
Person.say_hi(a1)

# "Person Guerilla says hi

We can do the same in JavaScript (using Array methods with "array like" objects like arguments easily comes to mind), but it's a bit more verbose, e.g:
Array.prototype.slice.call(arguments, 3);

Sunday 10 April 2022

Your Iterator is also an Iterable

I already explained in this previous post that generators in JavaScript and Python work in a very similar way (a generator function creates a generator object, that is both an iterable and a iterator).

Most times, for Iterable objects that have not been created with a generator fuction (hence, are not generator objects), the Iterator that we obtain from them is a different object, and we can create different iterators over the same iterable and iterate independently. What is pretty interesting is that Python's iteration protocol dictates that iterators have to be iterables also. That means that calling iter() passing on an iterator will return that iterator. From here:

The strangest fact about iterators is that they are also iterables.
When you pass an iterator to the iter function it'll return itself back

JavaScript iteration protocol is less demanding, as it does not require iterators to be iterable. However, I've recently learned here that all ES6 built-in iterators follow the python approach, so they are iterables with a [Symbol.iterator] method that returns the iterator:


[Symbol.iterator]() {
    return this;
}

An example:


> let ar = ["a", "b"];
undefined
> let iterator = ar[Symbol.iterator]();
undefined
> let iterator2 = iterator[Symbol.iterator]();
undefined
> iterator === iterator2;
true

Another interesting thing that I've recently learnt, this time in Python, is that the file object (I tend to call it file-handle) obtained when opening a a file in read mode is both and iterable and an iterator (so it's as if it had been created by a generator function)


>>> fh = open("commands.txt", "r")
>>> fh is iter(fh)
True

Sunday 3 April 2022

The Hamidian Massacres

I have a profound affection for the Armenian people. It comes down to a long time ago when I first knew about the Armenian Genocide of the early XX century. Over the years this feeling has not done but to increase. Since the day I admitted that regardless of not being a believer and still considering that there are some dubious elements in Christianity, I belong to a civilization deeply shaped by Christianity, a civilization I'm proud of, the story of resistence and survival of this people feels deeply inspiring to me. A Christian nation (the first one) partially surrounded by the eternal enemy of the Western world, Turkey (Azerbijan is populated by the same Turkish people and hence it's just the same enemy).

When many Armenians had to flee their land to avoid being completely annihilated by the Turks in 1917, many of them arrived to Marseille and settled in France. Since them, they have been an important contributor to France. They embraced the French culture and assimilated into the French nation, a nation defined by culture, values and the acceptance of being the continuation of the history that defined all that, not by genes or phenotypes. It's the same that most Polish, Portuguese, Italians, Spaniards, East Asians... and some Africans have done. At the same time, they have kept the links with the land of their ancestors and the pride of descending from a people of survivors.

In spite of the important contributions of the Armenian diaspora in countries like the USA and France, being a small country means being left apart in the Geopolitical game. That's why when Turkey-Azerbaijan invaded Armenian territory in october 2020, slaughtering the population helped by their ISIS mercenaries, nobody seemed to care. Moreover, we all know that when Muslims kill Christians (or atheists, or yazidis, or whatever), reacting to that is a clear sign (and sin) of Islamophobia... Also, when Turkish ultra-nationalists living in France attack French people of Armenian descent, that's not a big deal either... after all that's part of their culture and we have to respect it, right?

With the above and with the fact that many countries (among them Spain and UK!!!!) have not recognized yet the Armenian Genocide, it's not strange that previous massacres perpetrated by the Turks against the Armenians are hardly known. Indeed, I have to admit that I did not know about it until recently, when I watched this excellent video in Le Figaro (it tries to explain why the Turkish-Azeri invasion of Armenia did not prompt the same reactions as the Russian invasion of Ukraine).

So already at the end of the XIX century, the Turks, guided by ultra-nationalism and Islamism (as usual) decided to massacre quite a few thousands (between 100.000 and 300.000) Armenians during the Hamidian Massacres. As they would do 20 years later, they turned their hatred into pure anti-Christian violence, killing an additional 25.000 Assyrians (seems this time the Greeks were excluded, the Turkish would wait 2 decades to show their feelings of "respect, love and peace" to them, during the Greek Genocide). I have not much to say about this unknown massacres, just read the wikipedia article and remember it each time the neo-sultan Erdogan threatens us with his desire to join the European Union (to further destroy us from inside).