C ++, Java, C #, etc. have this really strange behavior regarding instance variables, in which the data (members or fields, depending on which culture you belong to) that are described in the class {} block refer to instances, while functions (well, methods but C ++ programmers seem to hate the term and instead say “member functions”), described in one block, belong to the class itself. Strange and confusing when you really think about it.
Many people do not think about it; they just accept it and keep moving on. But this actually causes confusion for many newbies who assume that everything inside the block belongs to instances. This leads to bizarre (experienced programmers) issues and problems associated with the overhead for each instance of these methods, and the difficulty of wrapping them around the whole implementation concept of "vtable". (Of course, this is mainly a collective mistake of teachers for not explaining that vtables is just one implementation, and in order not to give clear distinctions between classes and instances in the first place.)
Python does not have this confusion. Since functions (including methods) are objects in Python, it would be oddly inconsistent for the compiler to make that distinction. So what happens in Python is what you should intuitively expect: everything in the indented class block belongs to the class itself. And, yes, Python classes they themselves are objects (which gives a place to place these class attributes), and you do not need to skip over the standard library fairings to use them reflectively. (The lack of manifest is printed here quite freely.)
So, how do I hear you protest, are we really adding any data to the instances? Well, by default, Python does not limit you to adding anything to any instance. This does not even require you to make different instances of the same class with the same attributes. And this, of course, does not predetermine one block of memory to contain all the attributes of an object. In any case, it could contain links, given that Python is a pure semantics reference language, without C # style value types or Java style primitives.)
But obviously, it’s a good idea to do so, so the usual convention is to “add all the data at the time the instance is created and then not add more (or remove any attributes)”.
"When is it built"? Python really has no constructors in the sense of C ++ / Java / C #, since this lack of "reserved space" means that there is no real benefit to considering "initialization" as a separate task from its usual purpose - except that the advantage of initialization is that automatically happens to a new object.
So, in Python, our closest equivalent is the magic __init__ method, which is automatically called for newly created instances of the class. (There is another magic __new__ method, which behaves more like a constructor, in the sense that it is responsible for the actual creation of the object. However, in almost every case, we just want to delegate the base __new__ object, which calls some kind of built-in logic, in principle to give we need a small pointing ball that can serve as an object and point it to the class definition. Therefore, there is no point worrying about __new__ almost every case. It is more similar to operator new overloading for a class in C ++.) In the body of this method (not with initialization squeals in C ++ style, since there are no previously reserved data for initialization), we set the initial values for the attributes (and, possibly, do other work), based on the parameters that we give.
Now, if we want to be a little more careful about things, or efficiency is a real problem, there is another trick up our sleeve: we can use the __slots__ magic attribute for a class to specify the attribute names of the class. This is a list of strings, nothing unusual. However, this still does not initialize anything ; an instance does not have an attribute until you assign it. It just stops you from adding attributes with other names. You can even remove attributes from the object whose class specified __slots__ . All that happens is that the instances are given a different internal structure to optimize memory usage and attribute lookups.
Using __slots__ requires us to get out of the built-in type object , which we must do anyway (although this is not required in Python 2.x), this is for backward compatibility purposes only).
So now we can make the code work . But how to make it right for Python?
Firstly, just like in any other language, constantly commenting to explain already self-evident things, this is a bad idea. This distracts the user, and doesn’t really help you as a student of the language. You need to know what a class definition looks like, and if you need a comment to tell you that a class definition is a class definition, then reading the code comments is not the help you need.
With all of this “duck typing,” its poor form includes data type names in variable (or attribute) names. You are probably protesting, "but how should I track the type differently, without a manifest type declaration?" No. The code that uses your window list does not care that your window list is a list of windows. He just takes care that he can iterate over the list of windows and thus get values that can be used in certain ways related to windows. The way duck printing works: stop thinking about what the object is , and worry about what he can do .
In the code below, you will notice that I have entered the string conversion code in the House and Window constructors. This serves as a primitive form of type checking, and also ensures that we cannot forget to do the conversion. If someone tries to create a House with an identifier that cannot even be converted to a string, then it will throw an exception. It’s easier to ask for forgiveness, except permission, in the end. (Note that you really need to do some work in Python to create
As for the actual iteration ... in Python, we iterate over the objects in the container. Java and C # also have this concept, and you can get it together with the standard C ++ library (although many people are not worried). We do not iterate over indices, because it is a useless and distracting indirectness. We do not need to specify our windows_per_house values to use them; we just need to look at each value in turn.
What about the identification numbers, I hear what you are asking? Simply. Python provides us with an "enumerate" function that gives us (index, element) pairs, given the input sequence of elements). It is clean, it allows us to clearly indicate our need for an index to solve the problem (and the purpose of the index), and it is built in, which does not need interpretation, like all Python code, so it does not carry so much overhead. (When memory is a concern, a lazy-valued version can be used instead.)
But even then, the iterations to create each house, and then manually adding each of them to the initially empty list, are too low. Python knows how to build a list of values; we don’t need to tell how to do it. (And as a bonus, we usually get better performance by letting it do this part on its own, since the actual loop logic can now be executed internally, in native C.) Instead, we describe what we want in the list, with the list. We don’t need to go through the steps “each time to count each window count, make up the corresponding house and add it to the list”, because we can say “list of houses with the corresponding number of windows for each window-count in this input list”. This may be clunkier in English, but much cleaner in a programming language such as Python, because you can skip a bunch of small words and you don’t have to spend effort on describing the source list or the action of adding finished houses to the list. You do not describe the process at all, just the result. Made to order.
Finally, as a general programming concept, it makes sense, whenever possible, to postpone the construction of an object until we have everything that is necessary for the existence of this object. The "two-phase design" is ugly. Therefore, first we make windows for the house, and then at home (using these windows). With a list of concepts, it's simple: we just insert lists.
class House(object): __slots__ = ['ID', 'windows'] def __init__(self, id, windows): self.ID = str(id) self.windows = windows class Window(object): __slots__ = ['ID'] def __init__(self, id): self.ID = str(id) windows_per_house = [1, 3, 2, 1]