In this article, we tried to discuss the concept of classes and types. Of course, the content is very shallow and even more practical. However, this interpretation may be more helpful.

In the previous description of the water pipe model, I have always imagined the functional imaginary enemy as "procedural" programming. Of course, we will inevitably make some simple classifications of programming languages. Many people think that the opposite concept of functional programming is object-oriented programming, but there are many misunderstandings here. In the previous articles, I have always emphasized that if you want to use a new concept, you must at least explain it in that article. Along the way, functional basic concepts have helped us solve most of the problems. This up to the realization of the list, people feel acceptable. But later, in order to get some values more conveniently, some type annotations have been made. We must introduce the concept of classes here for the following reasons.

  1. Before obtaining List function head using head(pair) this syntax, then all kinds of nesting time, will reduce a lot of readability (though we realized compose / and_then a partial solution to this problem).
  2. For example, pair(a, b) , we are more like a stable structure of some data/values. The structure itself is stable. Then simply using the definition of functions will make their use too loose.
  3. When writing a code, the h(g(f(x))) often inconsistent with people's thinking logic. In fact, we consider f and then g . The call of the class allows us to complete this matter according to normal thinking and logic x.f().g().h() .
  4. Finally, there is the need for type annotation. The type annotation of Python mostly depends on the definition of related classes. So it is necessary to introduce the concept of classes.

However, we must first clarify what class and type different. In most languages, the type in a narrow sense refers to the value category that comes with the programming language; the type is an object-oriented concept, which is related to the concepts of what we call objects and instantiation. And if you look at most types tagging system, you can find the new class, is the creation of a new type . to understand, that is, 161764ef3d57cb type represents a collection of some values, and they have some properties in common. The class provides more concepts than the type , such as "attributes", "methods", and "inheritance". Of course, among Python , type also naturally assumes the metaclass , that is, class and class .

However, the class actually has both "procedural" and "functional" sides. For example, when we use the following student class, add_age involves changes to our own attributes, which involves the concept of "variables" or the house model we mentioned earlier. This is something we want to avoid.

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

    def add_age(self, add_num):
        self.age += add_num

Another issue involved is the concept of identity, which is the logic of how we judge whether two things are equal. Class programming. In fact, the result of each instantiation is a different result. For example, in the above example, we can try to define the following two students. Although their names and ages are the same, we find that they are not alone. .

>>> a = Student("a", 11)
>>> b = Student("a", 11)            
>>> a == b
False

In terms of technical implementation, Python compares the a and b , or that they are two houses, and Python compares the value of the house instead of the value inside. If we print a and b , we can find that they are different.

>>> a.__hash__()
137886633561
>>> b.__hash__()
137886633567

In the functional example, we only want to use the class as a data composition tool and provide some inheritance concepts, which is unnecessary. One way is to == the logic of 061764ef3d58c8. For example, in the above example, we need to override the __eq__ method:

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age  = age
    
    def add_age(self, add_num):
        self.age += add_num
    
    def __eq__(self, other):
        return (self.name == other.name) and (self.age == other.age)


>>> a = Student("a", 11) 
>>> b = Student("a", 11)
>>> a == b
True

A better way is to use the concept of dataclass Its name itself means that we are treating objects of this class as data. In the above example, we can use dataclass , and we can even omit the __init__ method:

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    age: int

We can find that by executing this way, we got the answer we were expecting before:

>>> a = Student("a", 11) 
>>> b = Student("a", 11)
>>> a == b
True

But dataclass does not limit your side-effect functions, such as add_age our example. In functional programming, we can use a Point Free writing method, by returning the object with modified parameters, for example, the above example can be rewritten as:

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    age: int

    def add_age(self, add_num):
        return Student(self.name, self.age + add_num)

This way of writing has the advantage that we can change it to chained calls. The disadvantage is that we may need to create a new variable name to store this result:

>>> Student("a", 1).add_age(2).add_age(3).add_age(-1)
Student(name='a', age=5)

This basically constitutes the main style behind this series of articles, except for a few indispensable side effects, and by isolating them to a small range. In other parts, we will make good use of the structural layering of object programming and the ability of project code management, as well as the functional features to solve most of the problems.


三次方根
1.2k 声望101 粉丝