python basic notes

First, inheritance

'''
1. What is inheritance
    Inheritance is a way of creating new classes. New classes are called subclasses and inherited classes are called base classes, parent classes and superclasses.
    Inheritance describes a "hereditary" relationship: subclasses can reuse attributes of the parent class

Inheritance in python pays attention to two points:
   1. Supporting a subclass inheriting multiple parent classes at the same time in python.
   2. There are two categories in python:
            New Class: But any class that inherits object and its subclasses... They are all new types.
                In Python 3, if a class does not inherit human classes, it defaults to inherit object classes, that is, all classes in Python 3 are new classes.

            Classic Classes: Classes that do not inherit object s, and subclasses of that class... They are all classical classes.
                It is only in Python 2 that new and classical classes can be distinguished

2. Why to use inheritance
     Reducing Code Redundancy
3. How to Use Inheritance

class Parent1(object):
    pass
class Parent2:
    pass
class Subclass1(Parent1,Parent2):
    pass
print(Subclass1.__bases__)


# 2. Priority of property search in the context of inheritance
      # When a class is a classical class, in the case of multiple inheritance, when the attributes to be searched do not exist, they will be searched in a depth-first manner.
      # When a class is a new type of class, in the case of multiple inheritance, when the attributes to be searched do not exist, they will be searched in a breadth-first way.
Inheritance solves the problem of code redundancy between classes. It must be that one class is a subclass of another class. By summarizing the similarities between objects, we can get classes. By summarizing the similarities between classes, we can get the parent class of classes.

Sequence of Attribute Search in Multi-Inheritance Context: Objects - "Classes of Objects -" is found one by one in a left-to-right order.
# In case of diamond inheritance problem, the difference between new class and classical class in attribute lookup is
# New classes: breadth-first lookup, top-level class in the last branch
# Classic Classes: Deep First Search, Find Top Classes in the First Branch

# One way to reuse parent functions in new methods derived from subclasses is:
# Functions that access a class by name
Note:
# 1. This method has nothing to do with inheritance.
# 2. Access is a function of a certain class and has no effect of automatically passing values.
class OldboyPeople:
    school='Oldboy'

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


class OldboyStudent(OldboyPeople):

    #           stu1,'Litterdam', 18,'female'
    def __init__(self,name,age,sex,num=0):
        OldboyPeople.__init__(self,name,age,sex) #OldboyPeople. _init_ (stu1, Litterdam', 18,'female')

        self.score=num

    def choose_course(self):
        print('%s is choosing course' %self.name)



class OldboyTeacher(OldboyPeople):

    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)

        self.level=level

    def score(self,stu,num):
        stu.score=num


stu1=OldboyStudent('Lee Dan',18,'female') #Oldboy Student. _init_ (stu1,'Litterdam', 18,'female')
print(stu1.__dict__)

tea1=OldboyTeacher('egon',18,'male',10) ##OldboyTeacher.__init__(tea1,'egon',18,'male',10)
print(tea1.__dict__)
code
Two ways to reuse parent functions in new methods derived from subclasses: only in subclasses
In Python 2: super (own class name, object itself)
In Python 3: super()
Calling super() results in a special object, which is specifically used to refer to attributes in the parent class,!!! Refer to the mro list!!!
Be careful:
# 1. This approach and inheritance depend strictly on the inheritance list of MrOS
# 2. Access is a binding method, which has the effect of automatically passing values.
class OldboyPeople:
    school='Oldboy'

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


class OldboyStudent(OldboyPeople):


    def __init__(self,name,age,sex,num=0):
        # OldboyPeople.__init__(self,name,age,sex) #OldboyPeople. _init_ (stu1, Litterdam', 18,'female')
        super(OldboyStudent,self).__init__(name,age,sex)

        self.score=num

    def choose_course(self):
        print('%s is choosing course' %self.name)



class OldboyTeacher(OldboyPeople):

    def __init__(self,name,age,sex,level):
        super().__init__(name,age,sex)

        self.level=level

    def score(self,stu,num):
        stu.score=num

#Example
class A:
    def test(self):
        print('A.test()')
        super().test()

class B:
    def test(self):
        print('from B')

class C(A,B):
    pass

obj=C()
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
obj.test()
'''
A.test()
from B
'''
code

Class inheritance has two meanings:

1. Change 2. Expansion

   Composition refers to an object having an attribute whose value is an object of another class.
   Using composition can reduce code redundancy between classes


Two. Polymorphism

1 polymorphism
Polymorphism refers to the multiple forms of the same thing.
2 polymorphism:
You can use objects directly without considering the specific types of objects.
Advantages: Normalization, simplification of object usage

Polymorphism is a concrete implementation mechanism of the two meanings of classes, that is, calling different classes to instantiate the same method under the object, and the implementation process is different.

The standard type in python is a good demonstration of the concept of polymorphism

import abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def speak(self):
        pass
    @abc.abstractmethod
    def run(self):
        pass
# Abstract base classes: are used to specify specifications, but all subclasses inheriting this class must implement speak and run, and the names must be spoke and run
# Note: The abstract base class cannot be instantiated
Animal()

class People(Animal):
    def speak(self):
        print('say hello')
    def run(self):
        pass
class Dog(Animal):
    def speak(self):
        print('Wang Wang Wang')
    def run(self):
        pass
class Pig(Animal):
    def speak(self):
        print('Hum, hum, hum')
    def run(self):
        pass
obj1=People()
obj2=Dog()
obj3=Pig()
# obj1,obj2,obj3 are all animals
obj1.speak()
obj2.speak()
obj3.speak()
def speak(animal):
    animal.speak()
speak(obj1)
speak(obj2)
speak(obj3)
obj1=[1,2,3]
obj2='hello'
obj3={'x':1}
print(obj1.__len__())
print(obj2.__len__())
print(obj3.__len__())
print(len(obj1))
print(len(obj2))
print(len(obj3))
code

Three, encapsulation

1 What is encapsulation
Installation is to store data attributes or function attributes in a namespace.
Enclosure refers to hiding, which is to distinguish between inside and outside, that is, hiding is not inside (hiding attributes can not be accessed directly outside the class, but can be accessed inside the class).
2 Why to encapsulate
1. Seal data attributes:???
2. Envelope function attributes:???
How to encapsulate???
Start with before the attributes defined in the class

The first level of encapsulation: class is sack, which itself is a kind of encapsulation

Encapsulation at the second level: Classes are defined as private and are only used internally and not accessible externally.

The third level of encapsulation: clear distinction between internal and external, internal implementation logic, external unknown, and provide an access interface for encapsulated logic for external use (this is the real encapsulation)

class People:
    __country='China' #_People__country='China'
    __n=111           #_People__n=111

    def __init__(self,name):
        self.__name=name #self._People__name=name

    def run(self):
        print('%s is running' %self.__name) #self._People__name

print(People.__country)

obj=People('egon')
print(obj.__name)
print(obj.run)
obj.run()

print(People.__dict__)
print(People._People__country)
print(obj.__dict__)
print(obj._People__name)
Attention should be paid to the following issues:
1. This hiding is only a grammatical distortion and does not really restrict access.
2. This transformation only occurs once when the grammar is checked in the class definition stage, and the attributes at the beginning of added after the class definition stage will not change.
3. In inheritance, if the parent does not want the subclass to override its own method, it can start with _ before the method.
The real intention of packaging:
Data attributes or function attributes are enclosed for later use, and enclosed or hidden for non-direct external use.
1. Seal data attributes: To hide data attributes is to prevent the external direct operation of hidden attributes, but indirectly operate attributes through the interface opened in the class. We can attach arbitrary control logic to the interface to strictly control the user's operation of attributes.
    2. Sealing function attributes: isolation complexity

IV. Category Methods and Decorators

 Functions defined in a class have two main types (three subcategories) of uses. One is the binding method, and the other is the non-binding method.

# 1. Binding method:
# Features: To whom the binding should be invoked, who will invoke who will automatically pass in as the first parameter.
# 1.1 Binding to Objects: Functions defined in classes are bound by default.
# 1.2 Binding to a class: Add a decorator class method to the function defined in the class


# 2. Non-binding methods
# Characteristic: Neither binding with class nor with object means that object or class can be called, but whoever calls it is a normal function, and there is no automatic value passing at all.
class Foo:
    def func1(self):
        print('Method to bind to an object',self)

    @classmethod
    def func2(cls):
        print('Methods bound to classes: ',cls)

    @staticmethod
    def func3():
        print('Ordinary function')

obj=Foo()
obj.func1()
print(obj)

Foo.func2()

# Binding method
print(obj.func1)
print(Foo.func2)

# Unbound method
print(obj.func3)
print(Foo.func3)

What is an ornament?

Apparatus are functions, decoration is decoration, meaning adding new functions to other functions

Decorator Definition: Essentially function, function is to add new functions to other functions

property decorator is to disguise a function attribute as a data attribute

1. Do not modify the source code of the decorated function (open and closed principle)

2. After adding new functions to the decorated function, the call mode of the decorated function is not changed.

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]

current_user={'username':None,'login':False}
def auth(auth_type='file'):
    def auth_deco(func):
        def wrapper(*args,**kwargs):
            if auth_type == 'file':
                if current_user['username'] and current_user['login']:
                    res=func(*args,**kwargs)
                    return res
                username=input('User name: ').strip()
                passwd=input('Password: ').strip()

                for index,user_dic in enumerate(user_list):
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_user['username']=username
                        current_user['login']=True
                        res=func(*args,**kwargs)
                        return res
                else:
                    print('Error in username or password,Re login')
            elif auth_type == 'ldap':
                print('Balabala Devil')
                res=func(*args,**kwargs)
                return res
        return wrapper
    return auth_deco


#auth(auth_type='file') is running a function and returning auth_deco, so @auth(auth_type='file')
#It's equivalent to @auth_deco, but now our auth_deco is used as a closure application, and the outer package auth leaves an auth_type='file'parameter for it.
@auth(auth_type='ldap')
def index():
    print('Welcome to the main page')

@auth(auth_type='ldap')
def home():
    print('This is your home.')

def shopping_car():
    print('Check the shopping cart.')

def order():
    print('Check the order.')

# print(user_list)
index()
# print(user_list)
home()

# Decorator with ginseng
def deco(func):
    print('---')
    func.x=1
    func.y=2
    func.z=3
    return func

#Everything is an object.
@deco    #test=deco(test)
def test():
    print('test_func_run')
test.x=1
print(test.__dict__)

@deco  #Foo=deco(Foo)
class Foo:
    pass

f1=Foo()
print(f1.__dict__)
print(Foo.__dict__)
print(f1.x)
def Typed(**kwargs):
    def deco(obj):
        # print('+++++',kwargs)
        # print('obj_name',obj)
        for key,val in kwargs.items():
            # obj.__dict__[key]=[val]
            # obj.key=val
            setattr(obj,key,val)
        return obj
    # print('---',kwargs)
    return deco

@Typed(x=1,y=2,z=3)   #Typed(x=1,y=2,x=3)---> @deco ---->Foo=deco(Foo)
class Foo:
    pass
print(Foo.__dict__)

# @deco
@Typed(name='egon')
class Bar:
    pass
print(Bar.name)

Decorator = higher-order function + function nesting + closure

@timer  #@ timer is equal to cal=timer(cal)
def cal(array):
    res=0
    for i in array:
        res+=i
    return res

cal(range(10))
'''
//Closure: Putting defining variables in a scope is equivalent to hitting a package
'''
def father(name):
    def son():
        # name='alex'
        print('My father is [%s]' %name)
        def grandson():
            # name='wupeiqi'
            print('My grandfather is [%s]' %name)
        grandson()
    son()

father('XXX')
class Room:
    tag="mmp"
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property
    def cal_area(self):
        # print('%s lives in% s total area is% s'% (self. owner, self. name, self. width * self. length))
        return self.width*self.length
    @property
    def cal_tiji(self):
        return self.length*self.width*self.heigh


    def test(cls):
        print('from test',self.name)

    @classmethod  # Approaches specifically for use by classes
    def tell_info(cls):
        print(cls)
        print('----->',cls.tag)


r1=Room('Toilet','av',100,100,10000)
r2=Room('Comfort station','abc',10,10,10)

Room.tell_info()  #In particular, classes are bound to instances when calling their own function properties.


print(Room.tag)
# print('%s lives in% s total area is% s'% (r1. owner, r1. name, r1. width * r1. length)

print(r1.cal_area)
print(r2.cal_area)
print(r1.cal_tiji)
print(r1.name)
print(r2.name)
class Room:
    tag="mmp"
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property  #Attribute method
    def cal_area(self):
        # print('%s lives in% s total area is% s'% (self. owner, self. name, self. width * self. length))
        return self.width*self.length
    @property
    def cal_tiji(self):
        return self.length*self.width*self.heigh


    @classmethod  # Class methods, methods specifically for use by classes
    def tell_info(cls,x):
        print(cls)
        print('----->',cls.tag,x)

    # def test(x,y):
    #     print(x,y)


    @staticmethod   #Static methods, class slave Toolkits
    def wash_body(a,b,c):
        print('%s %s %s Take a shower',(a,b,c))

Room.wash_body('alex','yuanhao','wupenqi')


r1=Room('Toilet','alex',100,100,10000)

V. Attribute Method

class List(list):
    def append(self, object):
        if type(object)  is str:
            # self.append(object)
            # list.append(self,object)
            super().append(object)
        else:
            print('Only strings can be added')
    def show_midlle(self):
        mid_index=int(len(self)/2)
        return  self[mid_index]

l1=List('helloworld')
l1.append('SB')
print(l1)
class Chinese:
    country='China'
    def __init__(self,name):
        self.name=name

    def play_ball(self,ball):
        print('%s hit %s' %(self.name,ball))

p1=Chinese('alex')
print(p1.__dict__)

#See
print(p1.name)
p1.play_ball('Basketball')

#increase

p1.age=18
print(p1.__dict__)
print(p1.age)

def test(self):
    print('Instance function attributes')
p1.test=test
print(p1.__dict__)
p1.test(p1)

# #Do not modify the underlying attribute dictionary
# p1.__dict__['sex']='male'
# print(p1.__dict__)
# print(p1.sex)

#modify
p1.age=19
print(p1.__dict__)
print(p1.age)

#delete
del p1.age
print(p1.__dict__)
class Chinese:
    contry='China'
    dang='gongchandang'
    def __init__(self,name):
        self.name=name

    def play_ball(self,ball):
        print('%s Beating %s'%(self,name))
#See
print(Chinese.contry)

#modify
Chinese.contry='japan'
print(Chinese.contry)
p1=Chinese('alex')
print(p1.__dict__)
print(p1.contry)

#delete
del Chinese.dang
del Chinese.contry
print(Chinese.__dict__)

#increase
def eat_food(self,food):
    Chinese.eat=eat_food()
    print(Chinese.__dict__)

    p1.eat('aaa')
Built-in method:
__str__: Triggered automatically when the object is printed, and then the return value of the binding method(Must be a string type)As a result of this printing
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return '<name:%s age:%s>' %(self.name,self.age)

obj1=People('egon',18)
obj2=People('lxx',38)

print(obj1) #print(obj1.__str__())
print(obj2) #print(obj2.__str__())

__del__: Automatic trigger before object is deleted, Within this method, the recovery of system resources related to this object should be performed
class Foo:
    def __init__(self,filename,encoding='utf-8'):
        self.f=open(filename,'r',encoding=encoding)

    def __del__(self):
        # print('run.....')
        self.f.close()

obj=Foo()
del obj #obj.__del__()

#obj.__del__()


6. Higher-order functions, iterators, generators

Definition of higher order function
1. The parameter received by a function is a function name.
2. The return value of a function is a function name.
3. Any of the above conditions can be called higher-order functions.

import  time
def Foo():
    print('------')

def test(func):
    print(func)
    start_time=time.time()
    func()
    stop_time=time.time()
    print('func_runtime %s' %(stop_time-start_time))

Foo()
test(Foo)  #Modify the way function is called
def run():
    print('from_run')

run()

def test():
    yield 1 #Pause, entrance
    print('from_run')
    yield 2
    print('from_run')

t=test() Generator object
#Wake-up iterator
t.__next__()
next(t)  # yield10
t.send('123')
age=10
res=True if age>10 else False
# if form ternary expression
l=['a' for i in range(10)]  #List parsing
g_l=('a' for i in range(10)) #Generator Expressions
print(l)
def test():
    for i in range(4):
        yield 1

t=test()
class Foo:
    def __init__(self,n):
        self.n=n
    def __iter__(self):
        return self
    def __next__(self):
        if self.n>=100:
            raise StopIteration
        self.n+=1
        return self.n
# l = list('hello')
# for i in l:
    # print(i)


f1 = Foo(10)
for i in f1: #f1.__iter__()----------->iter(f1)
    print(i)

Seven. Reflection

Reflection refers to the ability of a program to access, detect and modify its own state or behavior (introspection).

isinstance(obj,cls) checks whether obj is an object like CLS

issubclass(sub, super) checks whether the subclass is a derived class of the superclass


Reflection in python object-oriented: Operating object-related properties in the form of strings. Everything in python is an object (reflection can be used)

Four functions for introspection

hasattr(object,name)
Determine whether there is a method or attribute corresponding to a name string in an object

getattr(object,name,default=None)
Get named attributes from objects; getattr(x,'y') is equivalent to x.y.
When a default parameter is given, it is returned when the attribute is not given.
No, in this case an exception will be thrown.

setattr(x,y,v)
Sets the named property on a given object to the specified value.
setattr(x,'y', v) is equal to'x.y = v

delattr(x,y)
Delete named attributes from a given object.
delattr(x,'y') is equivalent to'del x.y'.


class Foo:
     pass

class Bar(Foo):
    pass

obj=Bar()

 print(isinstance(obj,Bar))
print(isinstance([],list))

print(issubclass(Bar,Foo))



# Reflection: Refers to manipulating attributes through strings
class Foo:
    def __init__(self,name):
        self.name=name


obj=Foo('eg')


# hasattr()
# print(hasattr(obj,'name')) #'name' in obj.__dict__

# getattr()
# print(getattr(obj,'name')) #obj.__dict__['name']
# print(getattr(obj,'age')) #obj.__dict__['age']
# print(getattr(obj,'age',None)) #obj.__dict__['age']

# setattr()
# setattr(obj,'age',18) #obj.age=18
# setattr(obj,'name','EG') #obj.name='EG'
# print(obj.__dict__)

# delattr()
# delattr(obj,'name')# del obj.name
# print(obj.__dict__)
code

Import other modules and use reflection to find out if there is a method for the module


Benefits of reflection

One advantage:

Implementing plug-and-play mechanism can define interfaces in advance. Interfaces will be implemented only after they are completed. This implements plug-and-play. This is actually a kind of "late binding". What does it mean? That is, you can write the main logic in advance (only define the interface), and then later implement the function of the interface.

Benefits two:

Dynamic Import Module (Based on Reflecting Current Module Members)

module_abc=__import__('dic.a.b.c')#Extracting File Names from Strings
# module_abc gets the top-level dic module
module_abc.a.b.c

8. _setattr_, _delattr_, _getattr__

Demonstration of the usage of the three:

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('----> from getattr:The attributes you are looking for do not exist')


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #That's infinite recursion. Think about it.
        # self.__dict__[key]=value #It should be used

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #Infinite recursion
        self.__dict__.pop(item)

#_ setattr_ Adding/modifying attributes triggers its execution
f1=Foo(10)
print(f1.__dict__) # Because you rewrite _setattr_, any assignment will trigger its operation, you did not write anything, is not assignment, unless you directly operate the attribute dictionary, otherwise you can never assign value.
f1.z=3
print(f1.__dict__)

#_ delattr_ triggers when attributes are deleted
f1.__dict__['a']=3#We can directly modify the attribute dictionary to complete the operation of adding/modifying attributes.
del f1.a
print(f1.__dict__)

#_ getattr_ triggers only when the calling property is used and the property does not exist
f1.xxxxxx

To grant authorization:

Authorization is a feature of packaging. Packaging a type is usually customized to existing types, which can create, modify or delete functions of the original product. Others remain the same. The process of authorization is that all the updated functions are handled by some part of the new class, but the existing functions are authorized to the default attributes of the object.

The key to authorization is to override the _getattr_ method

The main way to authorize is to _getattr_ map undefined methods to real built-in function file s
import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
        self.mode=mode
        self.encoding=encoding
    def write(self,line):
        print('---------',line)
        t=time.strftime('%Y-%m-%d %X')
        self.file.write('%s %s' %(t,line))

    def __getattr__(self, item):
        # print(item,type(item))
        return getattr(self.file,item)

    # def read(self):
    #     pass


f1=FileHandle('a.txt','r+')
print(f1.__dict__)
print('--->',f1.read) # Triggering getattr
# print(f1.write)
f1.write('11111111211111\n')
# f1.seek(0)
# sys_f=open('b.txt','w+')
#The main way to authorize is to _getattr_ map undefined methods to real built-in function file s
class List:
    def __init__(self,seq,permission=False):
        self.seq=seq
        self.permission=permission
    def clear(self):
        if not self.permission:
            raise PermissionError('not allow the operation')
        self.seq.clear()

    def __getattr__(self, item):
        return getattr(self.seq,item)

    def __str__(self):
        return str(self.seq)
l=List([1,2,3])
# l.clear() #There is no permission at this time, throw an exception


l.permission=True
print(l)
l.clear()
print(l)

#Access insert method based on authorization
l.insert(0,-123)
print(l)

9. _getattribute__

Review _getattr__

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('What I'm doing is')
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #Attribute access that does not exist triggers _getattr__

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print('Whether it exists or not,I'll do it.')

f1=Foo(10)
f1.x
f1.xxxxxx

__getattribute__

When _getattribute_ and _getattr_ coexist, only _getattrbute_ will be executed unless _getattribute_ throws an exception AttributeError during execution.

Both of them appear at the same time.

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('What I'm doing is')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('Whether it exists or not,I'll do it.')
        raise AttributeError('Ha-ha')

f1=Foo(10)
f1.x
f1.xxxxxx


Descriptors (_get_, _set_, _delete_)


1. What is the descriptor?

Descriptor is essentially a new class in which at least one of _get_(), _set_(), _delete_() is implemented, which is also called descriptor protocol.

_ get_(): Triggers when an attribute is called
_ set_(): Triggers when assigning an attribute
_ delete_(): Triggers when del deletes attributes

class Foo: #Foo is a new class in Python 3. It implements three methods. This class is called a descriptor.
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

The function of a descriptor is to proxy the attributes of another class (the descriptor must be defined as the class attributes of this class, not in the constructor).

class Foo:
    def __get__(self, instance, owner):
        print('trigger get')
    def __set__(self, instance, value):
        print('trigger set')
    def __delete__(self, instance):
        print('trigger delete')

#A new class containing these three methods is called a descriptor, and instances generated by this class call/assign/delete attributes without triggering the three methods.
f1=Foo()
f1.name='egon'
f1.name
del f1.name
#Question: When and where will trigger the execution of these three methods?

class Foo:
    def __get__(self, instance, owner):
        print('---get Method')

    def __set__(self, instance, value):
        print('---set Method')
        instance.__dict__['x']=value

    def __delete__(self, instance):
        print('---delete Method')

class Bar:
    x=Foo()#Class attributes defined as another class
    def __init__(self,n):
        self.x=n

b1=Bar(10)
print(b1.__dict__)


A data descriptor: implements at least _get_() and _set_()

class Foo:
   def __set__(self, instance, value):
      print('set')
   def __get__(self, instance, owner):
      print('get')

Two non-data descriptors: no _set_()

 class Foo:
     def __get__(self, instance, owner):
         print('get')


Matters needing attention:
A descriptor itself should be defined as a new type class, and the proxy class should be a new type class.
Second, descriptors must be defined as class attributes of this class, not as constructors.
Thirdly, we should strictly follow the priority, which is from high to low.

1. categories of attributes
2. Data Descriptor
3. Instance attributes
4. Non-data descriptors
5. Unfound property trigger _getattr_()

#Descriptor Str
class Str:
    def __get__(self, instance, owner):
        print('Str call')
    def __set__(self, instance, value):
        print('Str Set up...')
    def __delete__(self, instance):
        print('Str delete...')

class People:
    name=Str()
    def __init__(self,name,age): #The name is proxied by the Str class and the age by the Int class.
        self.name=name
        self.age=age


#Based on the above demonstration, we already know that defining a descriptor in a class is a class attribute that exists in the class attribute dictionary, not the instance attribute dictionary.

#Now that the descriptor is defined as a class attribute, it must be invoked directly by the class name, right?
People.name #Well, calling the class attribute name is essentially calling the descriptor Str, triggering _get_()

People.name='egon' #And the assignment, I went, did not trigger _set_()
del People.name #Let's try del. I went and didn't trigger _delete_()
#CONCLUSION: Descriptors have no effect on classes - ----> The conclusion of being foolish at home

'''
//Reason: Descriptors are defined as class attributes of another class when they are used, so class attributes have higher priority than class attributes disguised by secondary processed descriptors.
People.name #Well, call the class attribute name, find the class attribute name disguised by the descriptor, and trigger _get_()

People.name='egon' #What about assignment? It assigns a class attribute directly, which has a higher priority, which is equivalent to overwriting the descriptor. It certainly does not trigger the _set_() of the descriptor.
del People.name #Ditto
'''

//Class Attribute > Data Descriptor
#Descriptor Str
class Str:
    def __get__(self, instance, owner):
        print('Str call')
    def __set__(self, instance, value):
        print('Str Set up...')
    def __delete__(self, instance):
        print('Str delete...')

class People:
    name=Str()
    def __init__(self,name,age): #The name is proxied by the Str class and the age by the Int class.
        self.name=name
        self.age=age


p1=People('egon',18)

#If the descriptor is a data descriptor (i.e. _get_ and _set_), then the invocation and assignment of p1.name are trigger descriptor operations, which are independent of P1 itself and are equivalent to covering the attributes of the instance.
p1.name='egonnnnnn'
p1.name
print(p1.__dict__)#There is no name in the attribute dictionary of an instance because name is a data descriptor with priority over the instance attribute. View/assignment/deletion are all related to the descriptor and are not related to the instance.
del p1.name

//Data Descriptor > Instance Properties

Descriptor application

python is a weakly typed language, i.e. parameter assignment has no type restriction and can be implemented by descriptor mechanism.

class Type:
    def __init__(self,key,expected_type):
        self.key=key
        self.expected_type=expected_type

    def __get__(self, instance, owner):
        print('get_methods')
        # print('getinstance parameter [% s]'%instance)
        # print('owner parameter [% s]'%owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        print('set_methods')
        # print('setinstance parameter [% s]'% instance)
        # print('value parameter [% s]'% value)
        if not isinstance(value,self.expected_type) :
            # print('The type you pass in is not a string, error')
            # return
            raise TypeError('You don't pass in strings.')
        instance.__dict__[self.key]=value

    def __delete__(self, instance):
        print('delete_methods')
        # print('instance_arg[%s]'%instance)
        instance.__dict__.pop(self.key)

class People:
    name=Type('name',str)
    age=Type(18,int)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,33.3)
p1.name='alex'
print(p1.__dict__)
p1.age

Conclusion:

Descriptors can implement the underlying functions of most python class features, including @classmethod,@staticmethd,@property and even _slots_ attributes.

Describing the parent is one of the most important tools in many advanced libraries and frameworks, and descriptors are usually used as a component in a large framework of decorators or metaclasses.

class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #Class to call, instance to None,owner to class itself, instance to call, instance to instance, owner to class itself,
        def feedback():
            print('You can add functions here....')
            return self.func(owner)
        return feedback

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls):
        print('How do you do,Handsome guy %s' %cls.name)

People.say_hi()

p1=People()
p1.say_hi()
#Question, if there are parameters for class methods, let's say, let's say.

class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #Class to call, instance to None,owner to class itself, instance to call, instance to instance, owner to class itself,
        def feedback(*args,**kwargs):
            print('You can add functions here....')
            return self.func(owner,*args,**kwargs)
        return feedback

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls,msg):
        print('How do you do,Handsome guy %s %s' %(cls.name,msg))

People.say_hi('You're the thief who stole your heart.')

p1=People()
p1.say_hi('You're the thief who stole your heart.')

//Make your own @class method
Customize @ClassMethod
class StaticMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #Class to call, instance to None,owner to class itself, instance to call, instance to instance, owner to class itself,
        def feedback(*args,**kwargs):
            print('You can add functions here....')
            return self.func(*args,**kwargs)
        return feedback

class People:
    @StaticMethod# say_hi=StaticMethod(say_hi)
    def say_hi(x,y,z):
        print('------>',x,y,z)

People.say_hi(1,2,3)

p1=People()
p1.say_hi(4,5,6)

Customize @StaticMethod

item Series Methods

class Foo:
    def __getitem__(self, item):
        print('getitem')
        return self.__dict__[item]

    def __setitem__(self, key, value):
        print('setitem')
        self.__dict__[key]=value

    def __delitem__(self, key):
        print('delitem')
        self.__dict__.pop(key)

#Dictionary operation triggers item
#Triggering attr
f1= Foo()
print(f1.__dict__)
f1['name']='egon'
f1['22']='2222'

print(f1.__dict__)
class Foo:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]time,I execute')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key time,I execute')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alx'
print(f1.__dict__)


property

The essence of a static property is to implement three methods: get, set and delete.

#Implementing Type Detection Function

#First pass:
class People:
    def __init__(self,name):
        self.name=name

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

# p1=People('alex') #Property automatically implements that set and get methods belong to the data descriptor and have higher priority than instance attributes, so this writing will trigger the built-in set of property and throw an exception.


#Second Pass: Revised Edition

class People:
    def __init__(self,name):
        self.name=name #Instantiation triggers property

    @property
    def name(self):
        # return self.name #infinite recursion
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name is actually stored in self.DouNiWan
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__)

p1.name='egon'
print(p1.__dict__)

del p1.name
print(p1.__dict__)


#Third Pass: Add Type Check
class People:
    def __init__(self,name):
        self.name=name #Instantiation triggers property

    @property
    def name(self):
        # return self.name #infinite recursion
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            raise TypeError('Must be a string type')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name is actually stored in self.DouNiWan
p1.name=1


XIII, _str_, _repr_, _format_, _slots__

Change the object string to display _str_, _repr__

Self-customized formatted string _format__

format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#School Name - School Address - School Type
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#School Type: School Name: School Address
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#School Type/School Address/School Name
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','Beijing','private')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

'''
str Function or print function--->obj.__str__()
repr Or an interactive interpreter--->obj.__repr__()
//If _str_ is not defined, then _repr_ is used instead of output.
//Note: The return value of these two methods must be a string, or an exception will be thrown.
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))

1. _slots_ What is it?

    A class variable whose value can be a list, a meta-ancestor, or an iterative object, or a string (meaning that all instances have only one data attribute)
    Use points. To access attributes is essentially to access the _dict_ attribute dictionary of a class or object (the Dictionary of a class is shared, and each instance is independent)
2. Why use _slots_:
    Dictionaries take up a lot of memory. If you have a class with few attributes, but there are many instances, in order to save memory, you can use _slots_ instead of _dict s of instances.__
    When you define _slots_, the _slots_ will use a more compact internal representation for the instance. Instances are built through a small fixed-size array rather than defining a dictionary for each instance, much like tuples or lists. The attribute names listed in _slots_ are internally mapped to the specified small label of the array. One bad thing about using _slots_ is that we can no longer add new attributes to instances, but use only those attribute names defined in _slots_.
3. Notes:
    _ Many features of slots_ depend on ordinary dictionary-based implementations. In addition, classes defined after _slots_ no longer support some common class features, such as multi-inheritance. In most cases, you should only define _slots_ on classes that are often used as data structures, such as the millions of instance objects that need to be created for a class in a program.
A common misconception about _slots_ is that it can be used as an encapsulation tool to prevent users from adding new attributes to instances. Although using _slots_ can achieve this purpose, this is not its original intention. More is used as a memory optimization tool.
class Foo:
    __slots__='x'


f1=Foo()
f1.x=1
f1.y=2#Report errors
print(f1.__slots__) #f1 no longer has _dict__

class Bar:
    __slots__=['x','y']

n=Bar()
n.x,n.y=1,2
n.z=3#Report errors
class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#Both f1 and f2 have no attribute dictionary _dict_, so they are unified into _slots_ tube to save memory.

14, _next_, _iter_, _doc__

Fibonacci sequence

class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()

print(f1.__next__())
print(next(f1))
print(next(f1))

for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')

class Foo:
    'I'm describing information.'
    pass

class Bar(Foo):
    pass
print(Bar.__doc__) #This property cannot be inherited to subclasses

XV, _module_, _class_, _del__

_ Modul_ denotes the object of the current operation in that module

_ Class_ What is the class of the object that represents the current operation?

from lib.aa import C

obj = C()
print obj.__module__  # Output lib.aa, i.e. output module
print obj.__class__      # Output lib.aa.C, i.e. output class


_ The del_ destructor automatically triggers execution when an object is released in memory.
Note: If the generated object is only python program level (user level), then there is no need to define _del_. If the generated object also initiates system calls to the operating system, that is, an object has two resources, user level and kernel level, such as (opening a file, creating a database link), then it must. The _del is used to reclaim system resources while clearing objects.__

Typical application scenarios:

Create database class and instantiate database link object with this class. The object itself is stored in user space memory, while the link is managed by operating system and stored in kernel space memory.

When the program is finished, python will only reclaim its own memory space, i.e. user-mode memory, while the operating system resources are not reclaimed, which requires us to customize _del__ to initiate system calls to close database links to the operating system before the object is deleted, and to reclaim resources.

f=open('a.txt') # does two things, takes an f variable in user space and opens a file in operating system kernel space
 del f  Recycles only user space f, and the operating system file is still open

# So we should ensure that f.close() is executed before del f. Even if there is no del, the program will automatically del clean up resources after execution, so the correct use of file operation should be
f=open('a.txt')
Read and write...
f.close()
In many cases, it's easy to overlook f.close, which uses with context management.

XVI, _enter_, _exit_ and _call__

When manipulating file objects

with open('a.txt') as f:
   'code block'

This is called a context management protocol, which is the with statement. In order for an object to be compatible with the with statement, the _enter_ and _exit_ methods must be declared in the class of the object.

Context Management Protocol:

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

    def __enter__(self):
        print('Appear with Sentence,Object__enter__Triggered,If there is a return value, it is assigned to as Declared variables')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with Execute me when the Chinese code block is finished.')


with Open('a.txt') as f:
    print('=====>Execution code block')
    # print(f,f.name)

_ The three parameters in exit_() represent exception type, exception value and traceability information respectively. If an exception occurs in the code block in the with statement, the code after with cannot be executed.

If _exit() returns a value of True, the exception will be cleared as if nothing had happened, and the statement after with will execute normally.

class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        # print('enter')
        self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True
    def __getattr__(self, item):
        return getattr(self.f,item)

with Open('a.txt','w') as f:
    print(f)
    f.write('aaaaaa')#To grant authorization
    f.wasdf #Throw an exception and leave it to _exit__processing

Purpose:

1. The purpose of using with statement is to put code blocks into with to execute. After with is over, the cleaning work is automatically completed without manual intervention.

2. In programming environments where resources such as files, network connections and locks need to be managed, you can customize the mechanism of automatically releasing resources in _exit_. You don't have to deal with this problem anymore. This will be very useful.

The object is parenthesed to trigger execution of _call__

Note: The execution of the constructor is triggered by the creation object, that is, the object = the class name (); and the execution of the _call_ method is triggered by the object followed by parentheses, that is, the object () or the class ()) ().

class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # Execute _init__
obj()       # Execution of _call__

metaclass

Everything in python is an object.

All objects are instantiated, or call classes (the process of calling classes is called instantiation of classes)

The generation process of a class is actually the calling process of a metaclass.


The class keyword calls the metaclass type(...) when it creates the class for us, and the key component of the class passed in when it calls the type. A class has three major components, one is the metaclass type(...).

1. Class name class_name='xxxx'

2. Base classes class_bases=(object,)

3. Class namespace class_dic, which is obtained by executing class body code

The above three parameters are passed in turn when type is called


A class does not declare its own metaclass. By default, its metaclass is type. In addition to using built-in metaclass type, we can define metaclass by inheriting type. Then we can specify metaclass for a class by using metaclass keyword parameter. Classes defined by class are all objects (including object class itself is also metaclass type). An example that can be viewed with type(object)




Keywords: Python Attribute encoding Database

Added by NCllns on Fri, 13 Sep 2019 10:41:35 +0300