Self-describing data lets us then have generic operations. For instance, if an object hold a sequence of other object, we can create an operation which extracts the N-th one. And we can make that same operation work for an object which is a character string, vector, list. This is because the function can inquire the object what it is, and then call the appropriate concrete implementation which does the right thing for that type.
This leads us to realize that type is connected to the operations which are applicable. Those objects are somehow of the same "kind". This gives rise to the concept of subtyping and substitutability: a string is a kind of sequence and so is a list and we can have operations on sequences.
The non-object-oriented concepts are all that cruft from various languages which tries to strangle OO: virtual base classes, protected members, methods within class scope, scope resolution, copy constructors, yadda yadda ad nauseum ... We are led to believe that OO requires this or requires that. It's not OO if there is no encapsulation. It's not OO if there is no language-enforced "information hiding". It's not OO if there is no private/public access mechanism. It's not OO if method calls aren't represented as message passing. It's not OO if methods aren't in a compile-time class scope together with data members. ...