Class and object orientation

  • Class: An abstract collection, considered a template, in which properties and methods are defined
  • Object: Concrete instances based on classes

Class and class attributes

class Person:
    """
    class doc
    """
    classVar = 100

    def showMe(self):
        return __class__.__name__


print(Person)
print(type(Person))
print(Person.__name__)
print(Person.__doc__)
print(Person.showMe)
Copy the code

  • Class is followed by an identifier to define the class
  • Each class is actually an object, an object of class Type
  • ClassVar is a class variable shared by all instances and classes
  • The showMe method is essentially an object

instantiation

class Person:
    """
    class doc
    """
    classVar = 100

    def showMe(self):
        return __class__.__name__


p = Person()
print(p)
Copy the code

  • __main__ is the name of the file module, Person object represents the object of the Person class, and 0x to the end represents the object’s address in memory

  • Each instantiation generates a different object, even if the properties and methods in the instance are the same

  • The essence of instantiation is to call the class’s __init__ method, which cannot return a value

  • The Person class above does not define an __init__ method because it calls the __init__ method of the object class. Python3 does not define which class to inherit

  • Initializing an object can be seen as a two-step process. First, the __new__ method (inheriting object) is called to create operations such as storing the instance object in memory, and __init__ is used to fill the data of the instance. Only after the two steps are completed can the actual instantiation be considered

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
Copy the code
  • The first argument to the __init__ method, self, refers to the object created by the __new__ method in the first step, which may not be the self identifier. Self. name and self.age are instance attributes (instance variables) that take the name and age parameters, respectively
class Person:
    age = 3

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


tom = Person('Tom')
jerry = Person('Jerry')
print(tom.name, tom.age)
print(jerry.name, tom.age)
print(Person.age)
Person.age = 30
print(Person.age, tom.age, jerry.age)

Copy the code

  • Each instance variable value is unique to itself, and class variables are shared

Special attributes

Special attributes meaning
_name_ Name or module name
_class_ Instance Class information
_dict_ A dictionary of attributes for a class or instance
_qualname_ Simply put, the full path to the property
class Person:
    age = 3

    def __init__(self, name):
        self.name = name
        self.inner = self.__class__.Inner()

    class Inner:
        var = 100

        def test(self):
            pass


tom = Person('Tom')
jerry = Person('Jerry')

print(tom.__dict__)
print(tom.__class__)
print(tom.__class__.__name__)
print(__name__)
print(Person.__dict__)
print(Person.Inner.test.__qualname__)
Copy the code

  • Instance.__dict__ mainly outputs assignment statements defined under the __init__ method. Class.__dict__ outputs information about the class
  • Only class.__name__ output the class name, and print(_ alonename_) outputs the module name, which is the filename defined if the file is imported by another file
  • __class__ outputs the class information of the instance, and each class outputs the type class information
  • __qualname__ is the full path of an object, such as person.inner. Test for the test method

Attribute access rules

class Person: age = 3 height = 170 def __init__(self, name, age=18): Self. name = name self.age = age Tom = Person("Tom") Person.age, tom.age, jerry. Age) print(2, person. height, tom.height, Jerry. Height = 175 print(3, person. height, Tom. Height, Height += 10 print(4, Person. Height, Tom. Height, Person. Height += 15 print(5, Person. Height, Tom. Height, Person.weight = 70 print(6, person. weight, tom.weight, jerry. Weight) Tom. __dict__ [' height ']) # please print (8, Tom. __dict__ [' weight ']) # pleaseCopy the code
  • Person.age = 30 changes the value of the class variable. Tom uses the default 18 and Jerry passes in 20
  • 170, 170, 170, 170, 170, 170
  • Height =175 jerry.__dict__ add height=175 jerry
  • Height +=10 and add it to Tom.__dict__
  • 5 Output 185 180 175, for the class variable +=15, has no effect on the instance variable
  • 6 Print 70, 70, 70, dynamically add the weight attribute to the Person class
  • 7 Output 180, Tom._dict_[‘ height ‘] is equal to Tom. Height
  • This is not an object oriented error, this is a dictionary error, you can’t find the key of weight, and Tom. weight is found inside Python to help you find the class variable, so Tom. _dict_[‘weight’] should never be used

Class methods and static methods

  • Common methods
Class Person: def normal_function(): print(' normal ') def method(self): Normal_function () print(Person().normal_function) print(Person().normal_function()) print(Person().normal_function()) print(Person.__dict__)Copy the code
  • Person().normal_function() will return an error because normal_function() did not pass in an instance, and Person().normal_function can be treated as if the instance did not find the class attribute normal_function

  • Class method

class Person: @classmethod def class_method(cls): Print (' class methods) print (" {0. __name__} 's name = {0. __name__} ". The format (CLS)) CLS. HEIGHT = 170 # call Person. Class_method () Person().class_method() print(Person.__dict__) print(Person.HEIGHT) print(Person().HEIGHT)Copy the code
  • @classmethod means a classmethod, and both class and instance are accessible. CLS means a class, and CLS.HEIGHT = 170 creates a class variable

  • A static method

class Person: HEIGHT = 180 @staticmethod def static_method(): Person(). Static_method () print(person.__dict__)Copy the code
  • @staticmethod means that it is a staticmethod, with fewer parameter passing steps than a class method, and is accessible to both classes and instances

Private variables

  • Define private variables with “__”
class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
p = Person("tom", 25)
print(p.__name) 
print(p.__age)
Copy the code
  • Both p.__name and P. __age are reported above, essentially because Python changed __name instance variables to _Person__name and _Person__age. P.__dict__ can be seen

  • Protection members are defined by “_”

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
p = Person("tom", 25)
print(p._name) 
print(p._age)
Copy the code
  • It’s actually not protected, it’s still accessible, it’s just a convention

Attribute decorator

  • Properties are set to private variables and methods are set to value and pass values,
class Person:
    def __init__(self, name):
        self._name = name

    def name(self):
        return self._name

    def set_name(self, value):
        self._name = value
Copy the code
  • Python provides a property decorator to simplify calls
class Person:
    def __init__(self, name):
        self._name = name
    @property
    def name(self):
        return self._name
    @name.setter
    def set_name(self, value):
        self._name = value
Copy the code
  • The property decorator must come first, followed by the setter and deleter decorators
  • The property decorator is a simple way to turn an operation on a method into an access to a property, with some hidden effect

inheritance

  • Inheritance: Inherits properties and methods from other classes, and can also have their own properties and methods, reducing code redundancy
class Animal:
    def __init__(self, name):
        self._name = name

    def shout(self):
        print(f"{self._name} shout")

    @property
    def name(self):
        return self._name

    @name.setter
    def set_name(self, value):
        self._name = value


a = Animal("tom")
a.shout()


class Cat(Animal):
    pass


c = Cat("miao")
c.shout()


class Dog(Animal):
    pass


d = Dog("wang")
d.shout()

Copy the code
  • Animal is the parent of Cat and Dog, which are subclasses of Animal

  • Special attributes

Special properties and methods meaning
_bases_ The parents of class
_base_ __bases__ The first metaancestor
_mro_ The order in which methods are found
mro() Same as above
_subclasses_ () A list of subclasses of class
class A:
    pass
print(A.__base__)
print(A.__bases__)
print(A.mro())
print(A.__mro__)
print(int.__subclasses__())
print(bool.mro())
Copy the code

  • Access control in inheritance
class Animal:
    __a = 10
    _b = 20
    c = 30

    def __init__(self):
        print("self init")
        self.__d = 40
        self._e = 50
        self.f = 60
        self.__a += 1

    def showa(self):
        print(self.__a)
        print(self.__class__.__a)

    def __showb(self):
        print(self._b)
        print(self.__a)
        print(self.__class__.__a)


class Cat(Animal):
    __a = 100
    _b = 200


c = Cat()
c.showa()
c._Animal__showb()
print(c.c)
print(c._Animal__d)
print(c._e, c.f, c._Animal__a)
print(c.__dict__)
print(c.__class__.__dict__.keys())

Copy the code

  • C. showa():c calls the parent class showa. Cat is instantiated with Animal __init__. Self.__a +=1 is self._animal__a +=1. __a__ is found in Cat.__dict__, but not in Animal.__dict__. Method or instance invocation order: instance. dict_-> instance class. dict_-> parent class. dict_

  • With the search order, the output is easy. To sum up: instance.__dict__ can be understood as an assignment under the __init__ method. If a subclass does not override the __init__ method of its parent class, and the parent class has an assignment under its own __init__ method, the assignment on the instance is self._ parent name __a. Class.__dict__ can be understood as a collection of properties (not under __init__) and methods contained in a class.

  • Methods to rewrite

Shouts (self): print('Animal Shouts ') Class Cat(Animal): shouts (self): print('miao') def shout(self): print('miao2') a = Animal() a.shout() c = Cat() c.shout() print(Animal.__dict__) print(Cat.__dict__)Copy the code

  • The shout method of Cat overwrites the shout method of Animal, which is then overwritten by the shout method of its own class, essentially changing the method address pointed to by the shout variable in cat.__dict__

  • Enhancements to superclass methods

class Animal:
    def shout(self):
        print('Animal shouts')


class Cat(Animal):
    def shout(self):
        super(Cat, self).shout()
        super().shout()
        print('miao2')


a = Animal()
a.shout()
c = Cat()
c.shout()
print(Animal.__dict__)
print(Cat.__dict__)
Copy the code

  • Scenario: Subclasses want to inherit attributes from their parent class, but also want to have their own attributes
class A:
    def __init__(self, a):
        self.a = a


class B(A):
    def __init__(self, b, c):
        super(B, self).__init__(b + c)
        self.b = b
        self.c = c


b = B(2, 3)
print(b.a)
print(b.b)
print(b.c)

Copy the code
  • From the example above, the __init__ method of a subclass overrides the __init__ method of its parent class, so for a subclass to have the parent class’s A attribute, you need to call the __init__ method of the parent class

  • Multiple inheritance

zhuanlan.zhihu.com/p/268136917

  • mixin
Class Doc: def __init__(self, content): self. Content = content # def print(self): raise NotImplementedError() class Printable: def printContent(self): print(self.content) class Pdf(Printable,Doc): pass p = Pdf("pdf content") print(Pdf.mro()) p.printContent()Copy the code

Magic methods

_new_: Instantiates an object by inheriting the __new__ method of the object class, which allocates memory to store the object. The __init__ method allocates attributes to the object. The general case does not use this magic method, but involves the design pattern point case, need to use. Reference: juejin. Cn/post / 684490…

class SingleObject:
    def __new__(cls, *args, **kwargs):
        if not hasattr(SingleObject, "_instance"):
            SingleObject._instance = object.__new__(cls)
        return SingleObject._instance

    def __init__(self):
        pass


print(SingleObject.__dict__.keys())
a = SingleObject()
print(SingleObject.__dict__.keys())
b = SingleObject()
print(id(a))
print(id(b))
Copy the code

  • You can see that the class dictionary after a=SingleObject() has an additional _instance attribute

_str_: calls to the STR (), format(), and print() functions that return a string representation of an object. If not, the repr method is called to return a string representation; if not, the memory address of the object is returned

class Test: def __str__(self): # must return STR, __repr__ Return "STR method called" print(Test()) print("Test:{}". Format (Test())) print(STR (Test())) print([Test()])Copy the code

– When using instance objects in containers, the __repr__ magic method is looked for, and only the __repr__ magic method is overridden during development

_repr_: and __str__ magic method similar, return STR, development rewrite this magic method is good

class Test:
    def __repr__(self):
        return "repr method called"


print(Test())
print("Test:{}".format(Test()))
print(str(Test()))
print({Test()})
Copy the code

_bytes_: Returns a byte object

 def __bytes__(self):
        #return "{} is {}".format(self.name, self.age).encode()
        import json
        return json.dumps(self.__dict__).encode()
Copy the code

_BOOL_ : The built-in function bool(), or where the object is placed in a logical expression, returns a Boolean value when called. If you don’t define bool (), you look for len () to return length, if it’s not 0, it’s true. If len () is also undefined, all instances return true

class A:pass


print(bool(A()))
print(bool(A))

class B:
    def __bool__(self):
        return False

print(bool(B))
print(bool(B()))

class C:
    def __len__(self):
        return 0

print(bool(C()))
print(bool(C))


class D:
    def __bool__(self):
        return False

    def __len__(self):
        return 1

print(bool(D))
print(bool(D()))
Copy the code

Algorithm overload

class A:
    def __add__(self, other):
        return A(self.data + other.data)

    def __init__(self, data):
        self.data = data

    def __lt__(self, other):
        return self.data < other.data

    def __ge__(self, other):
        return self.data == other.data

    def __gt__(self, other):
        return self.data > other.data

    def __iadd__(self, other):
        return A(self.data + other.data)

    def __repr__(self):
        return f'A:[data={self.data}]'


a = A(2)
b = A(2)
print(id(a))
if a > b:
    print("a>b")
elif a == b:
    print("a==b")
else:
    print("a<b")
print(a + b)
print(id(a))
print(a)
a += b
print(id(a))
print(a)

Copy the code

  • Instances are unique by default, instance A is not equal to instance B, and the __eq__ method needs to be overridden
  • __add__ returns the point value without modifying the calling object. __iadd__ modifies the original object, which means that a points to the point instance

Context management

  • Enhancements to class instances when an object also implements _enterAnd _ _ ()exitThe _ () method, which is a context-managed object
class Point:
    def __init__(self):
        print("init")

    def __enter__(self):
        print("enter")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit")


with Point() as p:
    print("with")
Copy the code

  • You can see from the figure above that the __init__ method is executed when with Point()
  • When executing the code in the with block, the __enter__ method is executed first
  • The __exit__ method is executed after executing the code in the with block
  • The __exit__ method is executed even if an exception is thrown in the with block. Premise is __exit__ method to return True, the use of context: www.cnblogs.com/wongbingmin…
  • As p, like the operation file, is an alias. If the __enter__ method returns self, p is the alias of the Point() object. This means that the __enter__ method of the file object returns self.
class Point:
    def __init__(self):
        print("init")

    def __enter__(self):
        print("enter")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit")


with Point() as p:
    print("with")

with p as f:
    print(p)
    print(f)
    print(p == f)
    print(p is f)

f = open("timeDemo.py", "r")
print(f.__enter__())
with f as f1:
    print(f)
    print(f1)
    print(f == f1)
    print(f is f1)
Copy the code

Generator function

  • A function definition where a yield statement occurs is a generator function
def foo():
    a = 0
    while True:
        print("yield before")
        yield a
        print("yield after")
        a += 1


f = foo()
print(next(f))
print("~~~~~~")
print(next(f))
print("=====")
print(next(f))

Copy the code
  • From the code execution above, a call to next() returns yield to value when the code executes to the yield line, and yield to code execution the next time
  • Print the f object above and find that it is an iterator that returns the value every time next(). What is the use of the Python keyword yield? www.zhihu.com/question/34…

contextlib.contextmanager

  • Contextlib contextmanager it is a decorator to realize context management, decorate a function, rather than as a class _enter_ and _exit_ method. The function must yield, that is, the function must return a generator and only yield.
import contextlib @contextlib.contextmanager def foo(): # print('enter') try: Finally: print('exit') except: print("error") finally: print("error") finally: print('exit') print("quit") with foo() as f: raise Exception("error") print(f)Copy the code
  • The practical application
import contextlib
import datetime
import time


@contextlib.contextmanager
def timeit():
    print('enter')
    start = datetime.datetime.now()
    try:
        yield
    finally:
        print('exit')
        delta = (datetime.datetime.now() - start).total_seconds()
        print('delta = {}'.format(delta))


def add(x, y):
    time.sleep(2)
    return x + y


with timeit():
    add(4, 5)

Copy the code