A thorough understanding of Python object-oriented programming

Keywords: Python OOP

Before I started, I had been trying to find a popular and intuitive example to introduce object-oriented. Look around and find that everything can be object-oriented and nothing is object-oriented. Later, I found that the way human beings understand society is more object-oriented. "Birds of a feather flock together and people flock together", this sentence seems to have a good interpretation of our object-oriented. Birds can fly and fish can swim. People are always good at capturing the characteristics of various things in life and classifying them. This is actually an object-oriented idea.

Different objects always have different characteristics, and objects of the same class always have similar or the same characteristics

There is a saying called "everything is object", which means that programming should be advanced. Object orientation is a barrier we can't get around.

1. Process oriented and object-oriented

Goal: put the elephant in the refrigerator

1.1 process oriented

Process oriented is an event centered programming idea. When programming, the steps to solve the problem are analyzed and implemented step by step.

In this: our event is to put the elephant into the refrigerator, so we need to break it down into small steps and realize each small step

a = "elephant"
open_ice_door()  # To open the refrigerator door, you need to implement the function of opening the refrigerator door yourself
push(a)   # Push the elephant in
close_ice_door()  # To close the refrigerator door, you need to realize the function of closing the refrigerator door yourself

What if you put an elephant in the washing machine?

a = "elephant"
open_washer _door()  # To open the washing machine door, you need to realize the function of opening the washing machine door yourself
push(a)   # Push the elephant in
close_washer_door()  # To close the washing machine door, you need to realize the function of closing the washing machine door yourself

What if you put an elephant in an iron cage?

a = "elephant"
open_hot_door()  # To open the cage door, you need to realize the function of opening the cage door yourself
push(a)   # Push the elephant in
close_hot_door()  # To close the cage door, you need to realize the function of closing the cage door yourself

What if I want to turn off the refrigerator today, the washing machine tomorrow, and the cage the next day? What if I want to turn off the lion and the tiger? What if I want to turn off the elephant in the refrigerator, the lion in the washing machine, and the tiger in the cage?

We find that the requirements will become more and more complex, the amount of code will become more and more, and there will be more and more repetitive code. Moreover, in a really complex scenario, we can't write complete process oriented code.

At this time, smart developers began to give full play to their intelligence.

They found that the essence of this thing is to lock an animal into a container, which can open or close. The action of opening and closing the door is the same, and the container can be reused.

2.2 object oriented

In the above tasks: we need to create our own refrigerators, washing machines and cages, and realize the opening and closing methods.

Thus, we can encapsulate the general method

class Box():
    """Box class, which implements the method of opening and closing the door"""

    def open_door(self):
        pass

    def close_door(self):
        pass

class IceBox(Box):
    """Refrigerator"""

    def ice(self):
        """refrigeration"""
        pass

class WaterBox(Box):
    """Washing machine"""
    
    def add_water(self):
        """water"""
        pass
    
    def sub_water(self):
        """drainage"""
        pass   

    def wash(self):
        """wash"""
        pass
a = "elephant"
ice_box = IceBox()   # Refrigerator object
ice_box.open_door()  # Tell the refrigerator to open
push(a)   # Push the elephant in
ice_box.close_door()  # Notify the refrigerator to close
# What if I want to close the tiger?
b = "tiger"
ice_box.open_door()  # Tell the refrigerator to open
push(b)   # Push the tiger in
ice_box.close_door()  # Notify the refrigerator to close

The object-oriented idea is mainly based on the object, abstracting a problem into a specific object, and encapsulating the abstract object and object attributes and methods into a class.

For example, we can abstract the refrigerator, washing machine and iron cage into a box object, which can open or close the door.

Anyone who breaks away from process oriented and object-oriented is playing hooligans!

The object-oriented method is essentially process-oriented, because the computer problem-solving method is always process-oriented. Object-oriented is just a human carnival, just to make the program look more in line with people's way of thinking.

2. Class and object

Type 2.1

A class is a collection of related properties and behaviors

What is the most common class? Human!

Human attributes: two eyes, one nose, one mouth, two ears, one head, two hands and two legs

Human behavior: walking, running, jumping, breathing, eating

2.2 object

Class, created by the class.

Are humans human? No

Are you human? Yes

So. Human is an abstract class. You are a concrete human object.

Human beings are drawings drawn by Empress Nu Wa. The objects are villains pinched out by Empress Nu Wa one by one according to the drawings.

3. python object oriented

Programmer, do you have an object? No? Then create a new one yourself

3.1 definition of class

In fact, we have given many examples and defined many classes

In Python, you can use the class keyword to define classes.

The keyword class is followed by the class name. The class name is usually a word beginning with an uppercase letter, followed by (object), indicating which class the class inherits from. Generally, if there is no appropriate inherited class, the object class is used, which is the class that all classes will inherit in the end.

# The class name Person is usually a word that begins with a capital letter
# (object) indicates that it inherits from the object class. If you don't know the inheritance for the time being, you can skip it first
class Person(object):
    pass

Here we define a basic class. The functions written in the class are usually called (object) methods

3.2 creating objects

person = Person()  # Person is an instance object of the person class

3.3 method of object

The functions written in a class are often called (object) methods

class Person(object):
    def talk(self):
        print("I am an object method")
        
person = Person() 
person.talk()  # Use. To access the properties or methods of an object

3.3 object properties

class Person(object):
    def __init__(self, name, gender):
        print("This function is automatically executed when an object is created")
        self.name = name
        self.gender = gender
        
    def talk(self):
        print(f"My name is:{self.name},My gender is:{self.gender}")

__init _ is a special method. When an object is created, it will be initialized automatically. Its first parameter is always self, representing the instance itself.

>>> xiaoming = Person("Xiao Ming", "male")  # Create a boy object named Xiao Ming
# This function is automatically executed when an object is created

>>> print(xiaoming.name)
# Xiao Ming

>>> xiaoming.talk()
# My name is Xiao Ming. My gender is male


-----------------------------------------------------------------------
>>> xiaohong = Person("Xiao Hong", "female") # Create a girl object named Xiao Hong
# This function is automatically executed when an object is created

>>> print(xiaohong.name)
# Xiao Hong

>>> xiaoming.talk() 
# My name is Xiao Hong, and my gender is female



--------------------------------------------------------------------------
>>> xiaoli = Person("petty thief", "female") # Create a girl object named Xiao Li
# This function is automatically executed when an object is created

>>> print(xiaoli.name)
# petty thief

>>> xiaoli.talk()
# My name is Xiao Li. My gender is female

Here, we find that the instantiation process of a class is somewhat similar to that of giving birth to children. When we create an object, xiaoming = Person("Xiao Ming", "male") , we have a friend named Xiao Ming. He has his own name and gender, which is his attribute. When we want a little girl named Xiao Li, we can create a new object.

This is the famous one. There is no object. I new one myself.

In other languages, new is the keyword for creating objects.

3.4 what is self?

First of all, we should understand that self is not a keyword. In a class, you can also use other names without self. The reason why we named it self is just a custom among programmers. Abiding by this convention can make our code more readable.

What does the self parameter mean in our class?

self, the meaning of the English word is very obvious. It means yourself.

self represents the object itself in a class. Inside the class, access its internal properties and methods through this parameter.

# In this class, self represents a global variable within the scope of a class. Any method in this class can access the variables bound by self
# You can also access self bound functions
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.talk()  # Method to access self binding
        
    def talk(self):  # The parameter is self, and this function is the method of the object
        print(self.name)

4. Three object-oriented features

4.1 packaging

Encapsulation: encapsulate the objective things into abstract classes, hide the implementation details of attributes and methods, and only expose the interface.

The concept is awkward, but the idea is very simple.

Back to our refrigerators and washing machines, what are their common characteristics? It can hold things, open doors and close doors. These are their commonalities, so we can encapsulate them upward. Encapsulate things that can be loaded, closed and opened. And give him a general name: switchable box. A switchable box is a class. All objects of this class can hold things, open doors and close doors.

Encapsulation can assemble the data in the computer with the methods to operate the data, and encapsulate them in a module, that is, a class.

class Box():
    """Box class, which implements the method of opening and closing the door"""

    def open_door(self):
        pass

    def close_door(self):
        pass

4.2 succession

Inheritance: subclasses can use all the functions of the parent class and extend these functions. The process of inheritance is from general to special.

The idea of inheritance is also very simple. Have you ever thought about a question, why do you look like a human rather than a pig?

First of all, your father is human and your mother is human. Your parents all look human. If you inherit them, you will inherit all their attributes. You were born with these attributes common to mankind. And you can expand these attributes. For example, your father can only speak Chinese, but you can speak Chinese. You expand this method, and you can speak English.

Inheritance is simply a hierarchical model that can be reused. The upper layer of the hierarchy has generality, but the lower layer has particularity. In the process of inheritance, the class can inherit some methods and variables from the top part. In addition to inheriting, classes can also be modified or added. In this way, work efficiency can be effectively improved

class Father:

    def talk(self):
        print("I can speak")
    
    def breathe(self):
        print("I can breathe")
 
class Me(Father):
    pass
 

me = Me()  # Our Me class does not implement the following two methods, but inherits the methods of the Father class
me.talk()
me.breathe()
I can speak
 I can breathe

Composite inheritance:

class P1():
    def talk(self):
        print("I am p1")

class P2():
    def talk(self):
        print("I am p2")

class Person(P1, P2): # P1 comes first, call talk() of P1
    pass
        
p = Person()
p.talk()
# I'm p1
class P1():
    def talk(self):
        print("I am p1")

class P2():
    def talk(self):
        print("I am p2")

class Person(P2, P1): # P2 comes first, call P2's talk()
    pass
        
p = Person()
p.talk()
# I'm p2

Here's a small detail. When I inherit from multiple parent classes, multiple parent classes have the same method. Who will I call when I call?

In fact, it is in the order of inheriting parameters. Whoever comes first will call whose method

4.3 polymorphism

Polymorphism means that a class of things has multiple forms (an abstract class has multiple subclasses, so the concept of polymorphism depends on inheritance)

Your father has a talk() method, that is, speaking. You inherit your father's talk() method. For the same talk() method, your father speaks Chinese, you speak English, your brother speaks Russian and your sister speaks Korean. This is polymorphism

# Dad class
class Father:
    def talk(self):
        print("I can speak. I speak Chinese")

# Inherited from dad class
class Me(Father):
    def talk(self):
        print("I'm my brother. I speak English: hello,world")
 
# Inherited from dad class
class Son(Father):
    def talk(self):
        print("I'm my brother. I speak Russian:Всем привет")

# Inherited from dad class
class Sister(Father):
    def talk(self):
        print("I'm my sister. I speak Korean:전 세계 여러분 안녕하세요")
 
me = Me()
son = Son()
sister = Sister()
 
me.talk()
son.talk()
sister.talk()
I'm my brother. I speak English: hello,world
 I'm my brother. I speak Russian:Всем привет
I'm my sister. I speak Korean:전 세계 여러분 안녕하세요

Three necessary conditions for the existence of polymorphism

  1. There must be inheritance
  2. There should be rewriting;
  3. A parent class reference points to a child class object.

5. Attribute access rights

Sometimes, properties in a class do not want to be accessed externally. If you want the internal property not to be accessed externally, you can prefix the name of the property with two underscores _, In Python, if the variable name of an instance starts with a double underscore, it becomes a private variable, which can only be accessed internally, but not externally

5.1 front single underline_ xx

The underline on the front sheet has only the agreed meaning. The mutual agreement between programmers has no special meaning for the Python interpreter.

class Person(object):
    def __init__(self, name):
        self._name = "I am a pseudo private variable"

>>> p = Person()
>>> print(p._name)
I am a private variable

We see that the class does not prevent us from accessing variables_ name

So: names starting with a single underscore are just conventions in Python naming, indicating that they are for internal use. It usually has no special meaning for Python interpreters, just as a hint to programmers. Although I can be accessed, please treat me as a private variable and don't access it at will.

5.2 front double underline__ xx

If the variable name of an instance starts with a double underscore, it becomes a private variable, which can only be accessed internally, but not externally

class Person(object):
    def __init__(self):
        self.__name = "I am a private variable"

>>> p = Person()
>>> print(p.__name)
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 6, in <module>
    print(p.__name)
AttributeError: 'Person' object has no attribute '__name'

But we visit__ name, an error is reported, which prevents us from accessing private variables outside the instance. This ensures that the external code cannot modify the internal state of the object at will, so that the code is more robust through the protection of access restrictions

class Person(object):
    def __init__(self):
        self.__name = "I am a private variable"
    
    def __talk(self):
        print("sdsd")

p = Person()
p.__talk()
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 9, in <module>
    p.__talk()
AttributeError: 'Person' object has no attribute '__talk'

Is that really completely inaccessible? Not really

print(p._Person__name)
I am a private variable

Not directly accessible__ Name is because the Python interpreter__ The name variable has been changed to_ Person__name, so you can still pass_ Person__name to access__ Name variable:

However, it's best not to do so. Python's access restrictions are not strict, mainly by self-consciousness.

6. Built in special methods

Classes in Python provide many double underscores at the beginning and end__ xxx__ Methods. These built-in methods have been defined in the object class, and subclasses can be used directly.

__ xxx__ It is a system defined name with a "double underline" before and after it, which represents the special identification of special methods in python.

6.1 __init__(self, ...)

__ init__ Method is automatically executed when an object of the class is created, and there is no need for the user to call it. You can use this method to initialize your object.

class Person(object):
    def __init__(self, name):
        self.name = name
    
p = Person("test")
print(p.name)
# test

Equivalent to a constructor, the parameters we pass to the class are accepted in this function. And this method can only return None, not other objects.

But in fact, this method is only a pseudo constructor. It does not complete the process of generating objects. It only initializes the generated instance.

For example: we compare creating examples to having children. This function does not assume the responsibility of the mother to give birth to the child, but gives the child a name after the mother gives birth to the child. The real children are the following__ new__ method.

6.2 __new__(cls, *args, **kwargs)

__ new __ () at__ init __ () was called before, which is the real class constructor used to generate instantiated objects (empty attributes)__ new__ Method must return an object

This method will generate an instantiated object, and then our instance object will call__ init__ () method.

__ init__ And__ new__ difference:

  1. __ init__ It is usually used to initialize a new instance and control the initialization process, such as adding some properties and doing some additional operations, which occurs after the class instance is created. It is an instance level method.
  2. __ new__ It is usually used to control the process of generating a new instance. It is a class level method, and the instance generated by this method is actually__ init__ self inside

__ new__ Generally, it is rarely used in ordinary business scenarios, but more used in metaclasses, because it can deal with the generation process of objects at a lower level. And__ init__ More usage scenarios. Interested friends can learn more about the advanced playing methods of this method. There will be no introduction here.

6.3 __del__(self)

Destruct method. This method is automatically triggered when the object is released in memory. It is often used to "clean up the aftermath".

This method generally does not need to be customized, because Python has its own memory allocation and release mechanism, unless you need to specify some actions when releasing. The call of the destructor is automatically triggered by the interpreter during garbage collection.

This method will not be used in our work. So here we know that there is such a concept. At the same time, the Python interpreter has helped us with garbage collection and memory management. We don't need to over optimize memory use when programming in Python to avoid writing C + + style code.

6.4 __call__(self, *args, **kwargs)

Note: this method is not a built-in method. We need to implement this method

If we implement this method in a class, the instance of this class can be executed, and this method is executed. It's very awkward. Look at the code

class Person(object):

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

p = Person("test", 26)
p()  # Throw exception: class object is not callable
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 12, in <module>
    p()
TypeError: 'Person' object is not callable

Pass__ call__ Make class objects callable:

class Person(object):

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

    def __call__(self, *args, **kwargs):
        print("Execution instance method call method")

p = Person("test", 26)
p() # You can call the object of the class directly, because we implement it in the class__ call__ (), we are also calling__ call__ ()
# Execute instance method call method

6.5 __str__(self)

Returns a string expression for the object.

When learning python strings, we should be familiar with a method: str(), which can be used to convert an object into a string. In fact, what is executed is the of the object__ str__ method.

Not implemented__ str__ method:

# We didn't realize it__ str__  method
class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
p = Person("baozi", 20)
print(str(p))
# <__main__.Person object at 0x7fad74a98f28>
# By default, it returns some information about the instance when the interpreter executes, which has no reference significance

Realize__ str__ method:

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"my name is {self.name} age is {self.age}"

p = Person("baozi", 26)
print(str(p))
# my name is baozi age is 26

print(p) # To print this object directly, str(p) will also be executed first
# my name is baozi age is 26

__ str__ It is mainly used for str (object) to return a string

6.6 __repr__(self)

This method works much like str(). Both functions convert an instance into a string. But the difference is that the usage scenarios are different,

  • Among them__ str__ More emphasis on display. Therefore, when we print to the user or use STR function for type conversion, Python will call first by default__ str__ Function.

  • And__ repr__ More focus on the report of this instance. In addition to the contents of the instance, we often attach information related to its class, because these contents are for developers

If defined__ repr__ No definition__ str__, Then it should have been__ str__ The displayed string will be__ repr__ Replace.

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"my name is {self.name} age is {self.age}"
    
    def __repr__(self):
        return "Person('%s', %s)" % (self.name, self.age)

p = Person("baozi", 26)
print(p)
# my name is baozi age is 26

print(repr(p))
# Person('baozi', 26)

6.7 __eq__

This method is triggered when determining whether the values of two objects are equal

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        print("This function is triggered to judge whether two objects are equal")
        return True

p1 = Person("baozi", 26)
p2 = Person("baozi", 33)
print(p1 == p2) 
# This function is triggered to judge whether two objects are equal
# True

The above results show that the two objects are equal. But in fact, the attribute values of the two objects are different and should not be equal in theory. But we rewritten it__ eq__, Returns True in any case.

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        return self.__dict__ == other.__dict__


p1 = Person("baozi", 26)
p2 = Person("baozi", 33)
print(p1 == p2)
# False

p2.age = 26
print(p1 == p2)
# True

6.8 other comparison methods

Similar to 6.7, when the following operators are executed, they will also execute the corresponding special methods, which will not be expanded one by one here.

  • __ lt__ (): greater than (>)

  • __ gt__ (): less than (<)

  • __ le__ (): greater than or equal to (> =)

  • __ ge__ (): less than or equal to (< =)

  • __ eq__ (): equal to (= =)

  • __ ne__ (): not equal to (! =)

6.9 __getitem__(),__setitem__(),__delitem__()

Why put the three of them together? Because they are three swordsmen of dictionary value, assignment and deletion.

Special note: however, they are not object built-in methods.

Let's recall that the dictionary value is dict["key"]. In python, the value of dictionary is realized through []

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

p = Person("baozi", 26)
print(p["name"])
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 9, in <module>
    print(p["name"])
TypeError: 'Person' object is not subscriptable

We find that an error is reported when the value is directly obtained through []. Let's try adding three swordsmen

Add__ getitem__ ():

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __getitem__(self, key):
        print("adopt [] When taking value, I was called")
        return "hello,world"

p = Person("baozi", 26)
print(p["name"])
# When taking value through [], I was called
# hello,world

When we add__ getitem__ There is no error in the method!!, But at this time, no matter what value is taken, it returns hello and world. This also shows that when we get the value through p ["name"], the result is__ getitem__ ().

Conclusion: if an object is not implemented__ getitem__ Method, you cannot use a format such as p ["name"]

Draw inferences from one instance: we delete and assign the same

# assignment
p["name"] = "baozi2"
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 14, in <module>
    p["name"] = "baozi2"
TypeError: 'Person' object does not support item assignment
# delete
del p["name"]
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 15, in <module>
    del p["name"]
TypeError: 'Person' object does not support item deletion

The above still reports errors as usual.

Add__ setitem__ (),__ delitem__:

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __getitem__(self, key):
        print("adopt [] When taking value, I was called")
        return "hello,world"
    
    def __setitem__(self, key, value):
        print("adopt [] During assignment, I was called")
        return "hello,world"
    
    def __delitem__(self, key):
        print("adopt [] When deleting a value, I was called")
        return "hello,world"


p = Person("baozi", 26)
name = p["name"]
# When taking value through [], I was called

p["name"] = "baozi2"
# When assigned by [], I was called

del p["name"]
# When deleting a value through [], I was called

Finish! Our objects above can work like dictionaries. Of course, the above method does nothing. Here we mainly talk about usage! Learn to imitate by analogy.

6.10 __setattr__() , __delattr__(),__getattribute__():

Above we understand__ getitem__ (),__ setitem__ (),__ delitem__ (). The way they operate attributes is as follows: obj["key"]

In an object, we manipulate attributes in the form of obj.key. There are also three swordsmen in this operation mode: value taking, assignment and deletion. That's it__ setattr__ () , __ delattr__ (),__ getattribute__ ().

These three properties are built-in methods. We won't rewrite them if we have nothing to do

class Person(object):
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __getattribute__(self, key):
        print("Object pass . When taking value, I was called")
        return "hello,world"
   
	def __setattr__(self, key):
        print("I was called when the object was assigned a value")
    
    def __delattr__(self, key):
        print("When deleting object properties, I was called")

p = Person("baozi", 26)
print(p.name)
# When the object gets a value through. I was called
# hello,world

p.name = "change"
# I was called when the object was assigned a value

del p.name
# When deleting object properties, I was called

6.11 __getattr__

Our method is similar to__ getattribute__ () very similar. It may be a function called by a small partner when it takes a value.

However, it will be called when the value is taken, but there is a condition: it will be called only when the non-existent attribute is accessed

class Person(object):
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __getattr__(self, key):
        return "hello,world"
    
p = Person("baozi", 26)
print(p.cname) # To access a property that does not exist, call__ getattr__
# hello,world

And__ getattribute__ Called when any property is accessed.

7. Built in special attributes

7.1 __slots__

Using this feature, you can limit the attributes of class. For example, only the name and age attributes are allowed to be added to the Person instance. For the purpose of limitation, Python allows you to define a special class when defining a class__ slots__ Variable to limit the attributes that can be added to this class:

Before restriction:

class Person(object):

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

p = Person("test", 26)
p.sports = "Basketball, football"  # Here we add a new attribute to the object: sports
print(p.sports)
# Basketball, football

After restriction:

class Person(object):
    __slots__ = ("name", "age")

    def __init__(self, name, age):
        self.name = name
        self.age = age
        
p = Person("test", 26)
p.sports = "Basketball, football"
print(p.sports)
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 12, in <module>
    p.sports = "Basketball, football"
AttributeError: 'Person' object has no attribute 'sports'

An exception was thrown because the__ slots__ Attribute. Only name and age can be added

It should be noted that__ slots__ The restriction of is only valid for the object of the current class and has no effect on the subclass.

  • This attribute function looks very chicken. What's the use of it?

    Save memory and improve the search speed of attributes. It is usually used in ORM scenarios because there are many operations to create a large number of instances in these projects__ slots__ It will significantly reduce the use of memory and improve the speed. And with the increase of the number of instances, the effect will be more significant.

  • Why can it save memory space?

    Usually, the properties in our class exist__ dict__ In, it is a hash table structure, and the dynamic nature of python means that we need to divide more memory to ensure that we dynamically increase or decrease class attributes. But use__ slots__ Property, the compilation time can know in advance what properties this class has, so as to allocate a fixed space to store the known properties.

Despite__ slots__ It can save memory space and improve the access speed of attributes, but it also has limitations and side effects. Before use, we need to determine it according to the size of our business instance.

7.2 __dict__

List all members in the class or object!

This attribute, we only look at the name should be able to associate with anything. Yes, it's the structure of our dictionary.

In python classes, the attributes of classes and objects are stored mainly through dictionaries. Pass__ dict__ Property, we can get the attribute dictionary contained in the class.

class Person(object):

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


p = Person("baozi", 26)
print(Person.__dict__)  # A dictionary containing all class properties
{
    '__module__': '__main__', 
    '__init__': <function Person.__init__ at 0x7f9dd88b49d8>, 
    '__dict__': <attribute '__dict__' of 'Person' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Person' objects>,
    '__doc__': None
}

print(p.__dict__)  # A dictionary that contains all the attributes of an instance object
{'name': 'baozi', 'age': 26}

7.3 __doc__

Returns the annotation description of the class

class Person(object):
	pass

p = Person()
print(p.__doc__)
# None
class Person(object):
    """This is a class annotation"""  # Is to return the annotation description information here
	pass

p = Person()
print(p.__doc__)
# This is a class annotation

7.4 __class__

Returns an instance of which class the current object is

class Person(object):
	pass

p = Person()
print(p.__class__)
# <class '__main__.Person'>

7.5 __module__

Returns which module the object of the current operation belongs to

class Person(object):
	pass

p = Person()
print(p.__module__)
# __main__

8. Built in decorator

8.1 @property

Through the @ property decorator, you can access the method directly through the method name without adding a pair of "()" parentheses after the method name.

Modifies a method so that it can be accessed like a property.

Without decorator:

class Person(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
    
    def name(self):
        return self._name

p = Person("baozi", 26)
print(p.name)
# <bound method Person.name of <__main__.Person object at 0x7fb728643dd8>>

print(p.name())  # Without decorator, function must be called
# baozi

Add trimmer:

class Person(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
   
    @property
    def name(self):
        return self._name
    
p = Person("baozi", 26)
print(p.name)  # A decorator is added to directly access methods like accessing properties, without adding () calls
# baozi

Through this decorator, we can directly access methods like accessing properties.

So, what's the use of this ornament? Can't I just p.name()? It can also meet my needs

That's true. However, in terms of code readability, if we want to access the properties of an object, using p.name() is certainly not as intuitive as p.name.

His usage scenario is: we can use this decorator when we want to access the object properties and do not want the properties to be modified.

Expand: what if I want to change my age and my age needs some restrictions?

class Person(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
  	
    def set_age(self, age):
        if age <= 0:
            raise ValueError('age must be greater than zero')
        self._age = age
    
    def get_age(self):
        return self._age

Any problems? No problem. Can I play with the decorator just now? it's fine too

class Person(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
        
    @property
    def age(self):
        return self._age
  	
    @age.setter
    def age(self, age):
        if age <= 0:
            raise ValueError('age must be greater than zero')
        self._age = age

See here, the little partner may be a little confused@ age.setter what's sacred? How did you jump out? It is also a decorator. This decorator will be called when the attribute is assigned.

@*The. setter decorator must be after @ property, and the names of the two modified properties (functions) must be consistent* This is the function name

Using these two decorators, we can do a lot of things. For example: realize the ciphertext storage and plaintext output of password, judge whether the conditions are met before modifying attributes, and so on.

After adding the @ *. setter decorator and the @ property decorator to two functions with the same name:

  • When a class method is assigned as an attribute, the function corresponding to @ *. setter will be triggered
  • When a class method is read as a property, the function corresponding to @ property will be triggered

8.2 @staticmethod

Decorate the methods in the class as static methods, that is, when the class does not need to create an instance, it can be referenced directly through the class name.

class Person(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
     
    # This method can only be called from an instance of a class
    def talk(self):
        print(f"name is {self._name} age is {self._age}")
    
    # This method can be called directly through Person.talk() just like an ordinary function
    @staticmethod
    def static_talk(name, age): # There is no need to pass self here, and the function no longer needs to access the class
        print(f"name is {name} age is {age}")
p = Person("baozi", 26) # normal

p.static_talk("baozi", 26)  # An error is reported. This method is a static method and cannot be accessed through an instance
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 14, in <module>
    p.static_talk("baozi", 60)
TypeError: static_talk() takes 2 positional arguments but 3 were given
Person.static_talk("baozi", 60) # normal
    
Person.talk() # An error is reported. This method is not modified. It can only be accessed by instances, not classes
Traceback (most recent call last):
  File "/app/util-python/python-module/obj.py", line 15, in <module>
    Person.talk()
TypeError: talk() missing 1 required positional argument: 'self'

8.3 @classmethod

The method decorated by this decorator is a class method, not an instance method. What does this sentence mean? The methods we normally define belong to instance methods. You must create an instance before calling it. However, class methods can be accessed without instantiation.

The first argument to a class method is the class itself, not the instance

class Person(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
    
    @classmethod
    def static_talk(cls, name, age):
        print(f"name is {name} age is {age}")

Person.static_talk("baozi", 60)

Why does it look like our @ staticmethod function? In fact, students who pay attention to details have found it.

The function modified by @ classmethod has an additional parameter cls, which is different from our self. Self refers to the current instance and our cls refers to the current class.

  • @The method modified by classmethod needs to pass the current class object through the cls parameter. It can access class properties but not instance properties
  • @The method definition modified by staticmethod is the same as that of ordinary functions. It cannot access class properties or instance properties

What's the use of this ornament?

The most talked about on the Internet is to do and implement multi constructors. What is a multi constructor?

class Person(object):
    def __init__(self, age):
        self._age = age
    
    @classmethod
    def init_18_age_person_instance(cls):  # This is a class method. This method only creates objects that are 18 years old.
        age = 18
        return cls(age)
   
	@classmethod
    def init_30_age_person_instance(cls):  # This is a class method. This method only creates objects that are 30 years old.
        age = 30
        return cls(age)
        
p = Person(18) # An instance is created with attribute age = 18

p_18 = Person.init_18_age_person_instance() # An instance is also created here, with the attribute age = 18

p_30 = Person.init_30_age_person_instance() # An instance is also created here, with the attribute age = 30

Of course, I don't use the scene properly here, just to briefly explain its functions. Through this function, you can simulate multiple constructors. Specific business scenarios need you to explore more.

9. Try to understand that everything is an object

Through some of the built-in methods described above, we may have a further understanding of all objects.

At this time, we find that our str, int, dict, list and tuple are essentially one object. For different data structures, they rewrite their own set of built-in methods to achieve different functions.

For example, the dictionary dict ["key"] takes values in the following way:__ setitem__,__ getitem__…

For example, our string "hello,world" is not a static string. It is also an object. It also has many built-in methods. We can see "hello,world" because it is implemented__ str__ ()

After reading this, I believe that some small partners may have realized that as long as they always adhere to this idea to write code. You will make great progress.

Creation is not easy, and read and cherish. In case of any error or omission, please Haihan and contact the author for modification. The content is for reference. In case of infringement, please contact the author for deletion.

If the article is helpful to you, please use your hands. Your support is my greatest motivation.

Pay attention to the official account of small numbers: secretly learn, and kill them.

Posted by dylan001 on Mon, 22 Nov 2021 07:17:06 -0800