function
Definition of function
Remember "Everything is an object" in Python? Python treats functions as objects and can return from another function to build higher-order functions, such as: parameters are functions and return values are functions.
Let's start with the definition of a function.
- The function begins with the def keyword followed by the function name and parentheses ().
- The code executed by the function starts with a colon and is indented.
- return [expression] ends the function, optionally returning a value to the caller. return without expression is equivalent to returning None.
def functionname (parameters):
"Function Document String"
functionsuite
return [expression]
Calls to functions
def printme(str): print(str) printme("I want to call a user-defined function!") # I want to call user-defined functions! printme("Call the same function again") # Call the same function again temp = printme('hello') # hello print(temp) # None
Function Documentation
def MyFirstFunction(name): "Function Definition Procedure name Is a formal parameter" # Indicates occupying a parameter position print('Passed in{0}It's called an argument, it's a specific parameter value!'.format(name)) MyFirstFunction('My Programming Life') # My programmer's life passed in is called an argument, which is a specific parameter value! print(MyFirstFunction.__doc__) # name is a formal parameter in the function definition process help(MyFirstFunction) # Help on function MyFirstFunction in module __main__: # MyFirstFunction(name) # name is a formal parameter in the function definition process
Function parameters
Python functions have a very flexible and diverse form of parameters, which can be used to make simple calls and pass in very complex parameters.
- Positional argument
- Default argument
- Variable argument
- Keyword argument
- Named keyword argument
- Parameter combination
1.Location parameters
def functionname(arg1):
"Function Document String"
functionsuite
return [expression]
- arg1 - Positional parameters, which are fixed when calling a function.
2.Default parameters
def functionname(arg1, arg2=v):
"Function Document String"
functionsuite
return [expression]
- arg2 = v - Default parameter = Default value, when a function is called, the value of the default parameter is considered the default value if it is not passed in.
- Default parameters must be placed after positional parameters, or the program will error.
- Python allows the order of parameters when a function is called to be inconsistent with that when declared, because the Python interpreter can match parameter values with parameter names.
def printinfo(name, age=8): print('Name:{0},Age:{1}'.format(name, age)) printinfo('Xiao Ming') # Name: Xiaoming, Age:8 printinfo('Xiao Ming', 10) # Name: Xiaoming, Age:10 printinfo(age=12, name='Xiao Ming') # Name: Xiaoming, Age:12
3. Variable parameters
As the name implies, a variable parameter means that the number of parameters passed in is variable, ranging from 0, 1, 2 to any number, and is a variable length parameter.
def functionname(arg1, arg2=v, *args):
"Function Document String"
functionsuite
return [expression]
- args - Variable parameters, which can range from zero to any number, are automatically assembled into tuples.
- Variable names with an asterisk (*) hold all unnamed variable parameters.
def printinfo(arg1, *args): print(arg1) for var in args: print(var) printinfo(10) # 10 printinfo(70, 60, 50) # 70 # 60 # 50
4.Keyword parameters
def functionname(arg1, arg2=v, args, *kw):
"Function Document String"
functionsuite
return [expression]
- **kw - Key parameters, which can range from zero to any number, are automatically assembled into a dictionary.
def printinfo(arg1, *args, **kwargs): print(arg1) print(args) print(kwargs) printinfo(70, 60, 50) # 70 # (60, 50) # {} printinfo(70, 60, 50, a=1, b=2) # 70 # (60, 50) # {'a': 1, 'b': 2}
The similarities and differences between Variable Parameters and Keyword Parameters are summarized as follows:
- Variable parameters allow zero to any number of parameters to be passed in, which are automatically assembled into a tuple when the function is called.
- Keyword parameters allow you to pass in zero to any number of parameters that are automatically assembled into a dictionary (dict) within a function.
5.Named keyword parameters
def functionname(arg1, arg2=v, args, *, nkw, *kw):
"Function Document String"
functionsuite
return [expression]
- *, nkw - Named keyword parameter, keyword parameter that the user wants to enter, defined by adding a separator before nkw*.
- If you want to restrict the name of a keyword parameter, you can use Named Keyword Parameter.
- When using named keyword parameters, it is important to note that parameter names cannot be missing.
def printinfo(arg1, *,nkw, **kwargs): print(arg1) print(nkw) print(kwargs) printinfo(70, nkw=10, a=1, b=2) # 70 # 10 # {'a': 1, 'b': 2} printinfo(70, 10, a=1, b=2) # TypeError: printinfo() takes 1 positional argument but 2 were given
- There is no parameter name nwk written, so 10 is treated as a "location parameter", whereas the original function has only one location function and now calls two, so the program will make an error.
6.Parameter combination
Functions can be defined in Python using positional, default, variable, named keyword, and keyword parameters, four of which can be used together, but note that the order of parameter definitions must be:
- Location parameter, default parameter, variable parameter, and keyword parameter.
- Location parameter, default parameter, named keyword parameter, and keyword parameter.
Note the syntax for defining variable and keyword parameters:
- *args is a variable parameter, args receives a tuple
- **kw is a keyword parameter and kW receives a dict
Named keyword parameters are designed to restrict parameter names that callers can pass in while providing default values. Define named keyword parameters by not forgetting to write the delimiter*, otherwise position parameters are defined.
Warning: Although you can combine up to five parameters, don't use too many combinations at the same time, otherwise the function is difficult to understand.
Return value of function
def add(a, b): return a + b print(add(1, 2)) # 3 print(add([1, 2, 3], [4, 5, 6])) # [1, 2, 3, 4, 5, 6]
def printme(str): print(str) temp = printme('hello') # hello # No return, return None print(temp) # None print(type(temp)) # <class 'NoneType'>
Variable Scope
- In Python, a program variable is not accessible anywhere; access depends on where the variable is assigned.
- Variables defined within a function have a local scope, which is called a local variable.
- A variable defined outside a function has a global scope, which is called a global variable.
- Local variables can only be accessed within their declared functions, while global variables can be accessed throughout the program.
def discounts(price, rate): final_price = price * rate return final_price old_price = float(input('Please enter the original price:')) # 98 rate = float(input('Please enter a discount rate:')) # 0.9 new_price = discounts(old_price, rate) print('Price after discount is:%.2f' % new_price) # 88.20
num = 1 def fun1(): global num # Globalkeyword declaration is required print(num) # 1 num = 123 print(num) # 123 fun1() print(num) # 123
Nested Function
def outer(): print('outer The function is called here') def inner(): print('inner The function is called here') inner() # This function can only be called inside the outer function outer() # The outer function is called here # The inner function is called here
closure
It is an important grammatical structure in functional programming and a special embedded function.
If a variable with an outer non-global scope is referenced within an internal function, the internal function is considered a closure.
Closure provides access to variables in the outer non-global scope, which is called the closure scope.
def funX(x): def funY(y): return x * y return funY i = funX(8) print(type(i)) # <class 'function'> print(i(5)) # 40
# The return value of a closure is usually a function. def make_counter(init): counter = [init] def inc(): counter[0] += 1 def dec(): counter[0] -= 1 def get(): return counter[0] def reset(): counter[0] = init return inc, dec, get, reset inc, dec, get, reset = make_counter(0) inc() inc() inc() print(get()) # 3 dec() print(get()) # 2 reset() print(get()) # 0
# A nonlocal keyword is required if you want to modify variables in the closure scope def outer(): num = 10 def inner(): nonlocal num # nonlocal keyword declaration num = 100 print(num) inner() print(num) outer() # 100 # 100 (when nonlocal is not used, the output is 10)
recursion
- If a function calls itself internally, it is a recursive function.
# Utilize loops n = 5 for k in range(1, 5): n = n * k print(n) # 120 # Utilize Recursion def factorial(n): if n == 1: return 1 return n * factorial(n - 1) print(factorial(5)) # 120
# Fibonacci sequence f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1 # Utilize loops i = 0 j = 1 lst = list([i, j]) for k in range(2, 11): k = i + j lst.append(k) i = j j = k print(lst) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] # Utilize Recursion def recur_fibo(n): if n <= 1: return n return recur_fibo(n - 1) + recur_fibo(n - 2) lst = list() for k in range(11): lst.append(recur_fibo(k)) print(lst) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
# Set the number of recursive layers, Python default is 100 import sys sys.setrecursionlimit(1000)
The sys.setrecursionlimit () method is used to set the maximum depth of the Python interpreter stack to the required limit. This limit prevents any program from entering infinite recursion, which would otherwise overflow the C stack and crash Python.
Lambda expression
Definition of anonymous functions
There are two types of functions in Python:
- Class 1: Normal functions defined by def keywords
- Class 2: Anonymous functions defined with lambda keywords
Python uses the lambda keyword to create an anonymous function instead of the def keyword, which does not have a function name and has the following syntax:
lambda argument_list: expression
- lambda - A keyword that defines an anonymous function.
- argument_list - Function parameters, which can be position parameters, default parameters, keyword parameters, as well as parameter types in normal functions.
- :-Colon, add a colon between the function parameters and the expression.
- Expression - is just an expression, entering function parameters and outputting some values.
Be careful:
- There is no return statement in the expression because lambda does not need it to return, and the result of the expression itself is the return value.
- Anonymous functions have their own namespace and cannot access parameters outside their own parameter list or in the global namespace.
def sqr(x): return x ** 2 print(sqr) # <function sqr at 0x0000027519A80CA0> y = [sqr(x) for x in range(10)] print(y) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] lbd_sqr = lambda x: x ** 2 print(lbd_sqr) # <function <lambda> at 0x0000027519B693A0> y = [lbd_sqr(x) for x in range(10)] print(y) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] sumary = lambda arg1, arg2: arg1 + arg2 print(sumary(10, 20)) # 30 func = lambda *args: sum(args) print(func(1, 2, 3, 4, 5)) # 15
Application of Anonymous Functions
Functional programming means that every block of code is immutable and consists of pure functions, in which pure functions refer to functions that are independent of each other and do not affect each other. For the same input, there will always be the same output without any side effects.
# Non-functional programming def f(x): for i in range(0, len(x)): x[i] += 10 return x x = [1, 2, 3] f(x) print(x) # [11, 12, 13]
# Functional programming def f(x): y = [] for item in x: y.append(item + 10) return y x = [1, 2, 3] f(x) print(x) # [1, 2, 3]
Anonymous functions are often used in the high-order function of functional programming, which has two main forms:
- The parameter is a function (filter, map)
- The return value is a function (closure)
For example, in filter and map functions:
- filter(function, iterable) filters the sequence, filters out elements that do not meet the criteria, returns an iterator object, and if converted to a list, uses list() to convert.
odd = lambda x: x % 2 == 1 templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9]) print(list(templist)) # [1, 3, 5, 7, 9]
- map(function, *iterables) maps the specified sequence based on the function provided.
m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5]) print(list(m1)) # [1, 4, 9, 16, 25] m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]) print(list(m2)) # [3, 7, 11, 15, 19]
In addition to Python's built-in functions, we can also define higher-order functions ourselves.
def apply_to_list(fun, some_list): return fun(some_list) lst = [1, 2, 3, 4, 5] print(apply_to_list(sum, lst)) # 15 print(apply_to_list(len, lst)) # 5 print(apply_to_list(lambda x: sum(x) / len(x), lst))
Classes and Objects
Object=Property+Method
Objects are instances of classes. In other words, classes mainly define the structure of objects, and then we create objects using classes as templates. Classes contain not only method definitions, but also data shared by all instances.
- Encapsulation: Information Hiding Technology
We can use the keyword class to define a Python class, followed by the class name, semicolon, and class implementation.
class Turtle: # Class name conventions in Python begin with uppercase letters """A simple example of a class""" # attribute color = 'green' weight = 10 legs = 4 shell = True mouth = 'Big mouth' # Method def climb(self): print('I'm crawling very hard forward...') def run(self): print('I'm running fast forward...') def bite(self): print('Bite you to death!!') def eat(self): print('It's satisfying to have something to eat...') def sleep(self): print('Sleep, sleep, good night, zzz') tt = Turtle() print(tt) <__main__.Turtle object at 0x0000027519B90670> print(type(tt)) # <class '__main__.Turtle'> print(tt.__class__) # <class '__main__.Turtle'> print(tt.__class__.__name__) # Turtle tt.climb() # I'm crawling very hard forward... tt.run() # I'm running fast forward... tt.bite() # Bite you to death!! # Python classes are also objects. They are instances of type print(type(Turtle)) # <class 'type'>
- Inheritance: The mechanism by which subclasses automatically share data and methods between parent classes
class MyList(list): pass lst = MyList([1, 5, 2, 7, 8]) lst.append(9) lst.sort() print(lst) # [1, 2, 5, 7, 8, 9]
- Polymorphism: Different objects respond to different actions for the same method
class Animal: def run(self): raise AttributeError('Subclasses must implement this method') class People(Animal): def run(self): print('People are walking') class Pig(Animal): def run(self): print('pig is walking') class Dog(Animal): def run(self): print('dog is running') def func(animal): animal.run() func(Pig()) # pig is walking
What is self?
The self of Python is equivalent to the this pointer of C++.
class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt() # <__main__.Test object at 0x0000027519B900A0> # <class '__main__.Test'>
Class methods differ only from normal functions in that they must have an additional first parameter name (corresponding to the instance, that is, the object itself), which by convention is self. When calling a method, we do not need to explicitly provide the parameter corresponding to the parameter self.
class Ball: def setName(self, name): self.name = name def kick(self): print("My name is%s,Damn, who kicked me..." % self.name) a = Ball() a.setName("The Ball A") b = Ball() b.setName("The Ball B") c = Ball() c.setName("The Ball C") a.kick() # My name is Ball A. Damn, who kicked me... b.kick() # My name is Ball B, damn, who kicked me...
Python's Magic
It is said that Python objects are naturally given some magical methods, which are all about object-oriented Python...
They are a special way to add magic to your class...
If your object implements one of these methods, the method will be called by Python in special cases, and everything happens automatically...
Class has a magic method named u init_ (self[, param1, param2...]), which is called automatically when the class is instantiated.
class Ball: def __init__(self, name): self.name = name def kick(self): print("My name is%s,Damn, who kicked me..." % self.name) a = Ball("The Ball A") b = Ball("The Ball B") c = Ball("The Ball C") a.kick() # My name is Ball A. Damn, who kicked me... b.kick() # My name is Ball B, damn, who kicked me...
Public and Private
Defining a private variable in Python requires only two underscores u before the variable or function name, and the function or variable will be private.
class JustCounter: __secretCount = 0 # private variable publicCount = 0 # Open Variables def count(self): self.__secretCount += 1 self.publicCount += 1 print(self.__secretCount) counter = JustCounter() counter.count() # 1 counter.count() # 2 print(counter.publicCount) # 2 # Python's Private is Pseudo Private print(counter._JustCounter__secretCount) # 2 print(counter.__secretCount) # AttributeError: 'JustCounter' object has no attribute '__secretCount'
class Site: def __init__(self, name, url): self.name = name # public self.__url = url # private def who(self): print('name : ', self.name) print('url : ', self.__url) def __foo(self): # Private Method print('This is a private method') def foo(self): # Public method print('This is a public method') self.__foo() x = Site('My Programming Life', 'https://blog.csdn.net/xxx') x.who() # name: My Programming Life # url : https://blog.csdn.net/xxx x.foo() # This is a public method # This is a private method x.__foo() # AttributeError: 'Site' object has no attribute '__foo'
inherit
Python also supports inheritance of classes, and derived classes are defined as follows:
class DerivedClassName(BaseClassName):
statement-1
.
.
.
statement-N
BaseClassName (base class name) must be defined in one scope with a derived class. In addition to classes, expressions can also be used, which is useful when a base class is defined in another module:
class DerivedClassName(modname.BaseClassName):
statement-1
.
.
.
statement-N
# If a method or property with the same name as the parent class is defined in a subclass, the method or property corresponding to the parent class is automatically overwritten. # Class Definition class people: # Define basic properties name = '' age = 0 # Define private properties that are not directly accessible outside the class __weight = 0 # Define construction methods def __init__(self, n, a, w): self.name = n self.age = a self.__weight = w def speak(self): print("%s say: I %d Age." % (self.name, self.age)) # Single Inheritance Example class student(people): grade = '' def __init__(self, n, a, w, g): # Call the parent class's constructor people.__init__(self, n, a, w) self.grade = g # Override method of parent class def speak(self): print("%s say: I %d Aged, I'm reading %d grade" % (self.name, self.age, self.grade)) s = student('The programmed life of the pony', 10, 60, 5) s.speak() # The programmer of the pony said: I am 10 years old, I am in fifth grade
- Note: If the above program is removed: people._init_(self, n, a, w), the output is: I AM 0 years old, I am in fifth grade, because the construction method of the subclass overrides the construction method of the parent class.
import random class Fish: def __init__(self): self.x = random.randint(0, 10) self.y = random.randint(0, 10) def move(self): self.x -= 1 print("My Location", self.x, self.y) class GoldFish(Fish): # Goldfish pass class Carp(Fish): # carp pass class Salmon(Fish): # Salmon pass class Shark(Fish): # Shark def __init__(self): self.hungry = True def eat(self): if self.hungry: print("The dream to eat is to eat every day!") self.hungry = False else: print("Too much to eat!") self.hungry = True g = GoldFish() g.move() # My position 8 3 s = Shark() s.eat() # The dream to eat is to eat every day! s.move() # AttributeError: 'Shark' object has no attribute 'x'
There are two ways to solve this problem:
- Call unbound parent method Fish._init_(self)
- Use the super function super()._init_()
class Shark(Fish): # Shark def __init__(self): Fish.__init__(self) self.hungry = True def eat(self): if self.hungry: print("The dream to eat is to eat every day!") self.hungry = False else: print("Too much to eat!") self.hungry = True
class Shark(Fish): # Shark def __init__(self): super().__init__() self.hungry = True def eat(self): if self.hungry: print("The dream to eat is to eat every day!") self.hungry = False else: print("Too much to eat!") self.hungry = True
Although Python supports multiple forms of inheritance, we generally do not use multiple inheritance because it can be confusing.
class DerivedClassName(Base1, Base2, Base3):
statement-1
.
.
.
statement-N
It is important to note the order of the parent classes in parentheses. If the parent class has the same method name and is not specified when using a subclass, Python searches from left to right, that is, if a method is not found in a subclass, it searches from left to right for methods in the parent class.
# Class Definition class People: # Define basic properties name = '' age = 0 # Define private properties that are not directly accessible outside the class __weight = 0 # Define construction methods def __init__(self, n, a, w): self.name = n self.age = a self.__weight = w def speak(self): print("%s say: I %d Age." % (self.name, self.age)) # Single Inheritance Example class Student(People): grade = '' def __init__(self, n, a, w, g): # Call the parent class's constructor People.__init__(self, n, a, w) self.grade = g # Override method of parent class def speak(self): print("%s say: I %d Aged, I'm reading %d grade" % (self.name, self.age, self.grade)) # Another class, preparation before multiple inheritance class Speaker: topic = '' name = '' def __init__(self, n, t): self.name = n self.topic = t def speak(self): print("My name is %s,I am a speaker and the subject of my speech is %s" % (self.name, self.topic)) # multiple inheritance class Sample01(Speaker, Student): a = '' def __init__(self, n, a, w, g, t): Student.__init__(self, n, a, w, g) Speaker.__init__(self, n, t) # The method name is the same, and the default call is to precede the parent's method in parentheses test = Sample01("Tim", 15, 80, 9, "Python") test.speak() # My name is Tim, I'm a speaker, and the subject of my speech is Python class Sample02(Student, Speaker): a = '' def __init__(self, n, a, w, g, t): Student.__init__(self, n, a, w, g) Speaker.__init__(self, n, t) # The method name is the same, and the default call is to precede the parent's method in parentheses test = Sample02("Tim", 15, 80, 9, "Python") test.speak() # Tim said: I'm 15 and I'm in fourth grade
combination
class Turtle: def __init__(self, x): self.num = x class Fish: def __init__(self, x): self.num = x class Pool: def __init__(self, x, y): self.turtle = Turtle(x) self.fish = Fish(y) def print_num(self): print("Tortoise in sink%s Only, small fish%s strip" % (self.turtle.num, self.fish.num)) p = Pool(2, 3) p.print_num() # There are 2 turtles and 3 small fish in the pool
Classes, Class Objects, and Instance Objects
Class object and instance object
Class object: Create a class, in fact, an object opens up a space in memory, called a class object, class object has only one.
class A(object):
pass
Instance object: An object created by an instantiation class, called an instance object, can have more than one instance object.
class A(object): pass # The instantiated objects a, b, c belong to the instance object. a = A() b = A() c = A()
Class attributes: Variables defined outside the methods inside a class are called class attributes. Class attributes belong to class objects and share the same class attributes among multiple instance objects. In other words, class attributes can be shared by all objects instantiated through the class.
class A(): a = 0 #Class Properties def __init__(self, xx): A.a = xx #Use of class properties can be invoked through (class name. class properties).
Instance properties: Instance properties are related to a specific instance object, and one instance object and another instance object do not share properties. It is said that instance properties can only be used in their own objects, other objects cannot be used directly, because who called self, its value belongs to the object.
# create a class object class Test(object): class_attr = 100 # Class Properties def __init__(self): self.sl_attr = 100 # Instance Properties def func(self): print('Class object.Value of class attribute:', Test.class_attr) # Call Class Properties print('self.Value of class attribute', self.class_attr) # Equivalent to turning class attributes into instance attributes print('self.Value of Instance Property', self.sl_attr) # Call Instance Properties a = Test() a.func() # Class object. Value of class attribute: 100 # Value of self.class attribute 100 # self.Instance property value 100 b = Test() b.func() # Class object. Value of class attribute: 100 # Value of self.class attribute 100 # self.Instance property value 100 a.class_attr = 200 a.sl_attr = 200 a.func() # Class object. Value of class attribute: 100 # Value 200 for self.class attribute # self.Instance property value 200 b.func() # Class object. Value of class attribute: 100 # Value of self.class attribute 100 # self.Instance property value 100 Test.class_attr = 300 a.func() # Class object. Value of class attribute: 300 # Value 200 for self.class attribute # self.Instance property value 200 b.func() # Class object. Value of class attribute: 300 # Value 300 for self.class attribute # self.Instance property value 100
Note: Attributes and method names are the same, and attributes override methods.
class A: def x(self): print('x_man') aa = A() aa.x() # x_man aa.x = 1 print(aa.x) # 1 aa.x() # TypeError: 'int' object is not callable
What is a binding?
Python strictly requires that methods require instances to be invoked, and this limitation is Python's so-called binding concept.
The data properties of Python objects are usually stored in a dictionary named. u dict_ and can be accessed directly or obtained using Python's built-in function vars().
class CC: def setXY(self, x, y): self.x = x self.y = y def printXY(self): print(self.x, self.y) dd = CC() print(dd.__dict__) # {} print(vars(dd)) # {} print(CC.__dict__) # {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY': <function CC.printXY at 0x000000C3473C4F28>, # '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None} dd.setXY(4, 5) print(dd.__dict__) # {'x': 4, 'y': 5} print(vars(CC)) # {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, # '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None} print(CC.__dict__) # {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, # '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
Some related built-in functions (BIF s)
- The issubclass(class, classinfo) method is used to determine whether the parameter class is a subclass of the type parameter classinfo.
- A class is considered to be a subclass of itself.
- classinfo can be a tuple of class objects and returns True as long as class is a subclass of any of the candidate classes.
class A: pass class B(A): pass print(issubclass(B, A)) # True print(issubclass(B, B)) # True print(issubclass(A, B)) # False print(issubclass(B, object)) # True
- The isinstance(object, classinfo) method is used to determine whether an object is a known type, similar to type().
- type() does not consider a child to be a parent type, regardless of inheritance.
- isinstance() considers a subclass to be a parent type and considers inheritance.
- False is always returned if the first parameter is not an object.
- If the second parameter is not a class or tuple of class objects, a TypeError exception is thrown.
a = 2 print(isinstance(a, int)) # True print(isinstance(a, str)) # False print(isinstance(a, (str, int, list))) # True class A: pass class B(A): pass print(isinstance(A(), A)) # True print(type(A()) == A) # True print(isinstance(B(), A)) # True print(type(B()) == A) # False
- hasattr(object, name) is used to determine whether an object contains corresponding attributes.
class Coordinate: x = 10 y = -5 z = 0 point1 = Coordinate() print(hasattr(point1, 'x')) # True print(hasattr(point1, 'y')) # True print(hasattr(point1, 'z')) # True print(hasattr(point1, 'no')) # False
- getattr(object, name[, default]) returns an object property value.
class A(object): bar = 1 a = A() print(getattr(a, 'bar')) # 1 print(getattr(a, 'bar2', 3)) # 3 print(getattr(a, 'bar2')) # AttributeError: 'A' object has no attribute 'bar2'
class A(object): def set(self, a, b): x = a a = b b = x print(a, b) a = A() c = getattr(a, 'set') c(a='1', b='2') # 2 1
- setattr(object, name, value) corresponds to the function getattr(), which sets the value of an attribute that does not necessarily exist.
class A(object): bar = 1 a = A() print(getattr(a, 'bar')) # 1 setattr(a, 'bar', 5) print(a.bar) # 5 setattr(a, "age", 28) print(a.age) # 28
- delattr(object, name) is used to delete attributes.
class Coordinate: x = 10 y = -5 z = 0 point1 = Coordinate() print('x = ', point1.x) # x = 10 print('y = ', point1.y) # y = -5 print('z = ', point1.z) # z = 0 delattr(Coordinate, 'z') print('--delete z After Attribute--') # --After deleting the z attribute-- print('x = ', point1.x) # x = 10 print('y = ', point1.y) # y = -5 # Trigger error print('z = ', point1.z) # AttributeError: 'Coordinate' object has no attribute 'z'
- class property([fget[, fset[, fdel[, doc]]]]) is used to return property values in new classes.
- fget - Function to get attribute values
- fset - Function to set property values
- fdel - Delete Attribute Value Function
- doc - Attribute Description Information
class C(object): def __init__(self): self.__x = None def getx(self): return self.__x def setx(self, value): self.__x = value def delx(self): del self.__x x = property(getx, setx, delx, "I'm the 'x' property.") cc = C() cc.x = 2 print(cc.x) # 2 del cc.x print(cc.x) # AttributeError: 'C' object has no attribute '_C__x'
Magic Method
Magic methods are always surrounded by double underlines, such as _init_.
Magic methods are all about object-oriented Python, and if you don't know magic, you're not aware of the power of object-oriented Python.
The magic of magic methods is that they are always called automatically at the right time.
The first argument to the magic method should be cls (class method) or self (instance method).
- cls: the name representing a class
- self: the name representing an instance object
Basic magic
- The u init_ (self[,...]) constructor, the initialization method invoked when an instance is created
class Rectangle: def __init__(self, x, y): self.x = x self.y = y def getPeri(self): return (self.x + self.y) * 2 def getArea(self): return self.x * self.y rect = Rectangle(4, 5) print(rect.getPeri()) # 18 print(rect.getArea()) # 20
- The first method called by u new_u (cls[,...]) when an object is instantiated calls u new_u before calling u init_u initialization.
- _new_u requires at least one parameter, cls, representing the class to be instantiated, which is automatically provided by the Python interpreter at the time of instantiation and is passed directly to u init_.
- _new_u instantiates the current class and returns the instance to self of u init_. However, when u new_ is executed, it does not necessarily enter u init_u. Only when u new_ returns an instance of the current class cls, will u init_u of the current class enter.
class A(object): def __init__(self, value): print("into A __init__") self.value = value def __new__(cls, *args, **kwargs): print("into A __new__") print(cls) return object.__new__(cls) class B(A): def __init__(self, value): print("into B __init__") self.value = value def __new__(cls, *args, **kwargs): print("into B __new__") print(cls) return super().__new__(cls, *args, **kwargs) b = B(10) # <class>indicates that it is a class object. u main_u.B indicates that this class object is a class object named B in the current program. # Result: # into B __new__ # <class '__main__.B'> # into A __new__ # <class '__main__.B'> # into B __init__ class A(object): def __init__(self, value): print("into A __init__") self.value = value def __new__(cls, *args, **kwargs): print("into A __new__") print(cls) return object.__new__(cls) class B(A): def __init__(self, value): print("into B __init__") self.value = value def __new__(cls, *args, **kwargs): print("into B __new__") print(cls) return super().__new__(A, *args, **kwargs) # Changed cls to A b = B(10) # Result: # into B __new__ # <class '__main__.B'> # into A __new__ # <class '__main__.A'>
- If u new_u does not return an instance of the current class cls correctly, u init_u will not be invoked, not even the instance of the parent class, and no u init_ will be invoked.
class Earth: pass a = Earth() print(id(a)) # 3246693674912 b = Earth() print(id(b)) # 3246692922656 class Earth: __instance = None # Define a class attribute for judgment def __new__(cls): if cls.__instance is None: cls.__instance = object.__new__(cls) return cls.__instance else: return cls.__instance a = Earth() print(id(a)) # 3246693672464 b = Earth() print(id(b)) # 3246693672464
- The u new_u method primarily provides you with a way to customize the instantiation process of these classes when you inherit immutable classes (such as int, str, tuple)
class CapStr(str): def __new__(cls, string): string = string.upper() return str.__new__(cls, string) a = CapStr("i love python") print(a) # I LOVE PYTHON
- The u del_ (self) destructor, a method that is called when an object is about to be recycled by the system.
Python uses automatic reference counting (ARC)This method reclaims the space occupied by an object. When a variable in the program references the Python object, Python automatically guarantees that the object reference count is 1; when two variables in the program reference the Python object, Python automatically guarantees that the object reference count is 2, and so on, if the object reference count becomes 20 means that the object is no longer referenced by a variable in the program, indicating that the program no longer needs the object, so Python recycles it.
Most of the time, Python's ARC can accurately and efficiently recycle every object in the system. However, if a circular reference occurs in the system, such as object a holding an instance variable referencing object b and object b holding an instance variable referencing object a, then the reference counts for both objects are1. In fact, programs no longer have variable references to them, and the system should recycle them, so Python's garbage collector may not be as fast as it can, waiting for a dedicated Cyclic Garbage Collector to detect and recycle this reference cycle.
class C(object): def __init__(self): print('into C __init__') def __del__(self): print('into C __del__') c1 = C() # into C __init__ c2 = c1 c3 = c2 del c3 del c2 del c1 # into C __del__
-
__str__(self):
-
Trigger u str_u when you print an object
-
Trigger u str_u when you format with%s
-
Trigger u str_u when STR strongly converts a data type
-
__repr__(self):
-
repr is str's replacement
-
Execute u str_u when u str_ is present, execute u repr_u when u str_ is not implemented
-
The result of the repr(obj) built-in function is the return value of u repr_u
-
Trigger u repr_u when you format with%r
class Cat: """Define a cat class""" def __init__(self, new_name, new_age): """Called automatically after the object is created, It completes the initialization of the object""" self.name = new_name self.age = new_age def __str__(self): """Returns a description of an object""" return "Name is:%s , Age is:%d" % (self.name, self.age) def __repr__(self): """Returns a description of an object""" return "Cat:(%s,%d)" % (self.name, self.age) def eat(self): print("%s Eating fish...." % self.name) def drink(self): print("%s Drinking Coke..." % self.name) def introduce(self): print("Name is:%s, Age is:%d" % (self.name, self.age)) # An object was created tom = Cat("Tom", 30) print(tom) # The name is Tom and the age is 30 print(str(tom)) # The name is Tom and the age is 30 print(repr(tom)) # Cat: (Tom, 30) tom.eat() # Tom is eating fish... tom.introduce() # The name is Tom and the age is 30
The results returned by u str_ (self) are readable. That is, the meaning of u str_ is to get information that is easy for people to read, just like 2019-10-11 below.
The result returned by u repr_u(self) should be more accurate. In other words, u repr_ exists for debugging purposes and ease of use by developers.
import datetime today = datetime.date.today() print(str(today)) # 2021-10-03 print(repr(today)) # datetime.date(2021, 10, 3) print('%s' %today) # 2021-10-03 print('%r' %today) # datetime.date(2021, 10, 3)
Arithmetic Operators
Type factory functions refer to the creation of objects through functions rather than classes.
class C: pass print(type(len)) # <class 'builtin_function_or_method'> print(type(dir)) # <class 'builtin_function_or_method'> print(type(int)) # <class 'type'> print(type(list)) # <class 'type'> print(type(tuple)) # <class 'type'> print(type(C)) # <class 'type'> print(int('123')) # 123 # In this example, the list factory function processes a meta-ancestor object into a list object. print(list((1, 2, 3))) # [1, 2, 3]
- _u add_u (self, other) defines the behavior of addition: +
- _u sub_u(self, other) defines the behavior of subtraction: -
class MyClass: def __init__(self, height, weight): self.height = height self.weight = weight # Two objects add up in length and remain the same in width. Returns a new class def __add__(self, others): return MyClass(self.height + others.height, self.weight + others.weight) # Subtracts the width of two objects and keeps the length constant. Returns a new class def __sub__(self, others): return MyClass(self.height - others.height, self.weight - others.weight) # Speak about your parameters def intro(self): print("High to", self.height, " Re-to", self.weight) def main(): a = MyClass(height=10, weight=5) a.intro() b = MyClass(height=20, weight=10) b.intro() c = b - a c.intro() d = a + b d.intro() if __name__ == '__main__': main() # 10 for 5 # High 20 to 10 # 10 for 5 # Height 30 and weight 15
- _u mul_ (self, other) defines the behavior of multiplication: *
- _u truediv_ (self, other) Defines the behavior of true division: /
- _u floordiv_ (self, other) Defines the behavior of integer division: //
- _u mod_ (self, other) defines the behavior of the modularization algorithm:%
- _u divmod_ (self, other) defines the behavior when called by divmod()
divmod(a, b) combines the result of division with the result of the remainder operation and returns a tuple (a // b, a% b) containing the quotient and the remainder.
print(divmod(7, 2)) # (3, 1) print(divmod(8, 2)) # (4, 0)
- _u pow_ (self, other[, module]) defines the behavior when called by power() or ** operations
- _u lshift_u (self, other) defines the behavior of a bitwise left shift: < < <.
- _u rshift_u (self, other) defines the behavior of a bitwise right shift: >>
- _u and_u(self, other) Defines the behavior of bitwise and operations:&
- _u xor_ (self, other) defines the behavior of bitwise XOR operations: ^
- _u or_u(self, other) Defines the behavior of bitwise or operations: |
Inverse arithmetic operator
The inverse magic cube method maintains a one-to-one correspondence with the arithmetic operator, except that the magic method of the inverse operation has an additional "r". It is called when the file left operation does not support the corresponding operation.
- _u radd_ (self, other) defines the behavior of addition: +
- _u rsub_ (self, other) defines the behavior of subtraction: -
- _u rmul_u(self, other) Defines the behavior of multiplication: *
- _u rtruediv_ (self, other) Defines the behavior of true division: /
- _u rfloordiv_ (self, other) Defines the behavior of integer division: //
- _u rmod_u (self, other) defines the behavior of the modularization algorithm:%
- _u rdivmod_u (self, other) defines the behavior when called by divmod()
- _u rpow_ (self, other[, module]) defines the behavior when called by power() or ** operations
- _u rlshift_u (self, other) defines the behavior of a bitwise left shift: < <
- _u rrshift_u (self, other) defines the behavior of a bitwise right shift: >>
- _u rand_ (self, other) defines the behavior of bitwise and operations: &
- _u rxor_ (self, other) defines the behavior of bitwise XOR operations: ^
- _u ror_u (self, other) Defines the behavior of bitwise or operations: |
a + b
Here the additive is a and the added number is b, so a is active. The inverse operation is that if the u add_u() method of the a object is not implemented or does not support the corresponding operation, Python will call the u radd_() method of B.
class Nint(int): def __radd__(self, other): return int.__sub__(other, self) # Notice that self is behind a = Nint(5) b = Nint(3) print(a + b) # 8 print(1 + b) # -2
Incremental assignment operator
- _u iadd_ (self, other) defines the behavior of assignment addition: +=
- _u isub_ (self, other) defines the behavior of assignment subtraction: -=
- _u imul_u(self, other) Defines the behavior of assignment multiplication: *=
- _u itruediv_ (self, other) defines the behavior of assignment true division: /=
- _u ifloordiv_ (self, other) defines the behavior of assigning integer division: //=
- _u imod_u (self, other) defines the behavior of the modulus assignment algorithm:%=
- _u ipow_ (self, other[, modulo]) defines the behavior of assigning a power operation: **=
- _u ilshift_ (self, other) defines the behavior of assigning bitwise left shifts: <<=
- _u irshift_ (self, other) defines the behavior of assigning bitwise right shifts: >=
- _u iand_ (self, other) defines the behavior of assignment bitwise and operation: &=
- _u ixor_ (self, other) defines the behavior of assigning bitwise exclusive or operations: ^=
- _u ior_u (self, other) defines the behavior of assigning bits or operations: |=
Unary operator
- _u neg_u(self) Defines the behavior of a plus sign: +x
- _u pos_u(self) Defines the behavior of a negative sign: -x
- _u abs_u(self) defines the behavior when called by abs()
- _u invert_u(self) defines the behavior of bitwise negation: ~x
Property Access
- _u getattr_u (self, name): Defines the behavior of a user when attempting to acquire a property that does not exist.
- _u getattribute_u (self, name): Defines the behavior when a property of the class is accessed (invokes the method first to see if the property exists, and then u getattr_u if it does not exist).
- _u setattr_ (self, name, value): Defines the behavior when an attribute is set.
- _u delattr_ (self, name): Defines the behavior when an attribute is deleted.
class C: def __getattribute__(self, item): print('__getattribute__') return super().__getattribute__(item) def __getattr__(self, item): print('__getattr__') def __setattr__(self, key, value): print('__setattr__') super().__setattr__(key, value) def __delattr__(self, item): print('__delattr__') super().__delattr__(item) c = C() c.x # __getattribute__ # __getattr__ c.x = 1 # __setattr__ del c.x # __delattr__
descriptor
A descriptor is an attribute that assigns an instance of a particular type of class to another class.
- _u get_u (self, instance, owner) is used to access a property and returns the value of the property.
- The u set_u (self, instance, value) is called in the property assignment operation and does not return anything.
- The u del_ (self, instance) controls the deletion operation and does not return anything.
class MyDecriptor: def __get__(self, instance, owner): print('__get__', self, instance, owner) def __set__(self, instance, value): print('__set__', self, instance, value) def __delete__(self, instance): print('__delete__', self, instance) class Test: x = MyDecriptor() t = Test() t.x # __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> <class '__main__.Test'> t.x = 'x-man' # __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-man del t.x # __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>
Custom Sequence
Protocols are very similar to interfaces in other programming languages in that they specify which methods you must define. However, protocols in Python are less formal. In fact, protocols are more like a guide in Python.
Container Type Protocol
- If you want custom containers to be immutable, you only need to define u len_() and u getitem_() methods.
- If you want custom containers to be mutable, in addition to the u len_() and u getitem_() methods, you need to define u setitem_() and u delitem_() methods.
class CountList: def __init__(self, *args): self.values = [x for x in args] self.count = {}.fromkeys(range(len(self.values)), 0) def __len__(self): return len(self.values) def __getitem__(self, item): self.count[item] += 1 return self.values[item] c1 = CountList(1, 3, 5, 7, 9) c2 = CountList(2, 4, 6, 8, 10) print(c1[1]) # 3 print(c2[2]) # 6 print(c1[1] + c2[1]) # 7 print(c1.count) # {0: 0, 1: 2, 2: 0, 3: 0, 4: 0} print(c2.count) # {0: 0, 1: 1, 2: 1, 3: 0, 4: 0}
- _u len_u (self) defines the behavior (returning the number of elements in the container) when called by len().
- _u getitem_ (self, key) defines the behavior of getting elements in a container, equivalent to self[key].
- _u setitem_ (self, key, value) defines the behavior of setting the specified element in the container, equivalent to self[key] = value.
- _u delitem_ (self, key) defines the behavior of deleting a specified element from a container, equivalent to del self[key].
# Write a customized list of changes that require you to record how many times each element in the list has been visited class CountList: def __init__(self, *args): self.values = [x for x in args] self.count = {}.fromkeys(range(len(self.values)), 0) def __len__(self): return len(self.values) def __getitem__(self, item): self.count[item] += 1 return self.values[item] def __setitem__(self, key, value): self.values[key] = value def __delitem__(self, key): del self.values[key] for i in range(0, len(self.values)): if i >= key: self.count[i] = self.count[i + 1] self.count.pop(len(self.values)) c1 = CountList(1, 3, 5, 7, 9) c2 = CountList(2, 4, 6, 8, 10) print(c1[1]) # 3 print(c2[2]) # 6 c2[2] = 12 print(c1[1] + c2[2]) # 15 print(c1.count) # {0: 0, 1: 2, 2: 0, 3: 0, 4: 0} print(c2.count) # {0: 0, 1: 0, 2: 2, 3: 0, 4: 0} del c1[1] print(c1.count) # {0: 0, 1: 0, 2: 0, 3: 0}
iterator
- Iteration is one of Python's most powerful functions and a way to access collection elements.
- An iterator is an object that remembers the location of traversal.
- Iterator objects are accessed from the first element of the collection until all elements are accessed.
- Iterators can only move forward and not backward.
- String, list, or tuple objects can be used to create iterators:
string = 'python' for c in string: print(c) ''' p y t h o n ''' for c in iter(string): print(c)
links = {'B': 'Baidu', 'A': 'Ali', 'T': 'tencent'} for each in links: print('%s -> %s' % (each, links[each])) ''' B -> Baidu A -> Ali T -> tencent ''' for each in iter(links): print('%s -> %s' % (each, links[each]))
- Iterators have two basic methods: iter() and next().
- The iter(object) function is used to generate an iterator.
- next(iterator[, default]) returns the next item of the iterator.
- iterator - Iterable object
- Default - Optional to set the default value to be returned when there is no next element, and if not, the StopIteration exception will be triggered if there is no next element.
links = {'B': 'Baidu', 'A': 'Ali', 'T': 'tencent'} it = iter(links) while True: try: each = next(it) except StopIteration: break print(each) # B # A # T it = iter(links) print(next(it)) # B print(next(it)) # A print(next(it)) # T print(next(it)) # StopIteration
Using a class as an iterator requires two magic methods u iter_() and u next_() to be implemented in the class.
- _u iter_u(self) defines the behavior of elements in an iteration container and returns a special iterator object that implements the u next_() method and identifies the completion of iteration by a StopIteration exception.
_u next_() returns the next iterator object. - StopIteration exceptions are used to identify the completion of an iteration and prevent an infinite loop from occurring. In the u next_() method, we can set the StopIteration exception to be triggered to end the iteration after a specified number of cycles have been completed.
class Fibs: def __init__(self, n=10): self.a = 0 self.b = 1 self.n = n def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b if self.a > self.n: raise StopIteration return self.a fibs = Fibs(100) for each in fibs: print(each, end=' ') # 1 1 2 3 5 8 13 21 34 55 89
generator
- In Python, functions that use yield are called generator s.
- Unlike normal functions, a generator is a function that returns an iterator and can only be used for iteration operations. A simpler point understanding generator is an iterator.
- During the call generator run, the function pauses and saves all current run information each time yielding is encountered, returns the value of yielding, and continues to run from the current location the next time the next() method is executed.
- Calling a generator function returns an iterator object.
def myGen(): print('Generator Execution!') yield 1 yield 2 myG = myGen() for each in myG: print(each) ''' Generator Execution! 1 2 ''' myG = myGen() print(next(myG)) # Generator Execution! # 1 print(next(myG)) # 2 print(next(myG)) # StopIteration
def libs(n): a = 0 b = 1 while True: a, b = b, a + b if a > n: return yield a for each in libs(100): print(each, end=' ') # 1 1 2 3 5 8 13 21 34 55 89