**Background**

I've taught myself Python over the past year-and-a-bit, and would consider myself an intermediate Python user at this point, but never studied computing at school/university. As such, my knowledge of theory is a little weak. Python is the only language I know.

I'm trying to wrap my head around the Liskov Substitution Principle so that I can write better, more object-oriented code.

**Question 1: The Python data model**

As we know, in Python 3, all custom classes implicitly inherit from `object`. But the docstring of `object` includes this line:

> When called, it accepts no arguments and returns a new featureless instance that has no instance attributes and cannot be given any.

With this in mind, how can any python classes that accept a nonzero number of parameters for their constructors be said to comply with the LSP? If I have a class `Foo`, like so:

    class Foo:
        def __init__(self, bar):
            self.bar = bar

then, surely this class definition (and all others like it) violates the contract specified by `object`? `object` and `Foo` cannot be used interchangeably, as `object` accepts exactly 0 parameters to its constructor, while `Foo` accepts exactly 1.

**Question 2: `collections.Counter`**

As Raymond Hettinger [tells us][1], the Python dictionary is an excellent example of the open/closed principle. The `dict` class is "open for extension, closed for modification" -- if you wish to modify some of the prepackaged behaviours of a `dict`, you're advised to inherit from `collections.abc.MutableMapping` or `collections.UserDict` instead.

`collections.Counter`, however, is a [direct subclass][2] of `dict`. While it is mostly an extension of `dict` rather than a modification of behaviours already defined in `dict`, this isn't true for [`collection.Counter.fromkeys`][3]. With a standard `dict`, this classmethod is an alternative constructor for a dictionary, but the method is overridden in `Counter` so that it raises an exception. Here is the comment explaining why this is the case:

```none
# There is no equivalent method for counters because the semantics
# would be ambiguous in cases such as Counter.fromkeys('aaabbc', v=2).
# Initializing counters to zero values isn't necessary because zero
# is already the default value for counter lookups.  Initializing
# to one is easily accomplished with Counter(set(iterable)).  For
# more exotic cases, create a dictionary first using a dictionary
# comprehension or dict.fromkeys().
```

The explanation makes sense -- but surely this violates the LSP? According to the [Wikipedia article][4], the LSP states that "New exceptions cannot be thrown by the methods in the subtype, except if they are subtypes of exceptions thrown by the methods of the supertype." `dict.fromkeys` does not throw a `NotImplementedError`; `Counter.fromkeys` does.

**Comments**

I'm interested in whether these examples do, in fact, break the LSP. However, I'm also interested in *why* they break the LSP, if indeed they do. In what situations is enforcing the LSP necessary/advisable/useful? In what situations is worrying about the LSP more of a meaningless distraction? Etc.


  [1]: https://stackoverflow.com/a/38534939/13990016
  [2]: https://github.com/python/cpython/blob/01dea5f12b31862999217c091399a318f23b460a/Lib/collections/__init__.py#L522
  [3]: https://github.com/python/cpython/blob/01dea5f12b31862999217c091399a318f23b460a/Lib/collections/__init__.py#L637
  [4]: https://en.wikipedia.org/wiki/Liskov_substitution_principle