Object-oriented polymorphic python - Built-in functions related classes - the class Built Magic functions - iterator protocol - context management -04

Polymorphism

A thing with different forms

For example: water -> solid, liquid, gaseous

Polymorphic:# 多个不同对象可以相应同一个对象,产生不同的结果

First emphasized, polymorphism is not a special syntax, but a state, characteristics (a corresponding plurality of different objects may be the same method, different results of the body length)

Benefits: For users, the use of cost reduction

Under the previous USB interface, mouse, keyboard, they are multi-state

Interface type ducks abstract class can write code that includes a polymorphic (a duck simplest type)

'''
要管理 鸡 鸭 鹅
    如何能够最方便的管理,就是我说同一句话,他们都能理解
    他们拥有相同的方法
'''


class Chicken:
    @staticmethod
    def bark():
        print("咯咯咯咯")

    @staticmethod
    def spawn():
        print("下鸡蛋...")


class Duck:
    @staticmethod
    def bark():
        print("嘎嘎嘎")

    @staticmethod
    def spawn():
        print("下鸭蛋...")


class E:
    @staticmethod
    def bark():
        print("鹅鹅鹅鹅")

    @staticmethod
    def spawn():
        print("下鹅蛋...")


j = Chicken()
y = Duck()
e = E()


def mange(obj):
    obj.spawn()


mange(j)
# 下鸡蛋...
mange(y)
# 下鸭蛋...
mange(e)
# 下鹅蛋...

python common polymorphic (different object types with the same method, different results)

# 不管什么类型,他都与type这个方法  ---> python中多态的体现
# 多态在python中其实很常见,因为到处充斥着继承与组合
a = 10
b = '10'
c = [10]

print(type(a))
print(type(b))
print(type(c))
# <class 'int'>
# <class 'str'>
# <class 'list'>

Common built-in functions

  • isinstance
 # isinstance()  # 判断一个对象是不是某个类的实例
 # 参数1 要判断的对象,参数2 要判断的类型 
def add_num(a, b):
    # if type(a) == type(b):
    if isinstance(a, int) == isinstance(b, int):
        return a+b
    else:
        print("数据类型不符")


add_num("100", 10)
  • issubclass
# issubclass() # 判断一个类是不是另一个类的子类
#   参数一:子类,参数二:父类
class Animal:
    @staticmethod
    def eat():
        print("动物得吃东西...")


class Pig(Animal):
    @staticmethod
    def eat():
        print("猪吃东西...")


class Tree:
    @staticmethod
    def light():
        print("植物光合作用...")


def mange(obj):
    # if isinstance(obj, Animal):
    if issubclass(type(obj), Animal):
        obj.eat()
    else:
        print("不是动物...")


pig = Pig()
t = Tree
mange(pig)
# 猪吃东西...
mange(Tree)  # AttributeError: type object 'Tree' has no attribute 'eat'
# 不是动物...

Object-oriented functions built-in magic

  • __str__
'''
    __str__ 会在对象被转为字符串时,转换的结果就是这个函数的返回值
    使用场景:我们可以利用该函数来自定义,对象是打印格式
'''
class Person:
    def __str__(self):  # 重写object中的 __str__
        print("__str__ run")
        return 'abc'  # abc下面的报错那里就变成了 abc


p = Person()
# 所有的类都可以转成字符串
print(p)  # 打印了 __str__ run,又报错了
# __str__ run
# abc  # 写return 之前TypeError: __str__ returned non-string (type NoneType)  --> __str__ 必须要有一个str类型的返回值

str(p)  # 没有写print 在控制台也输出了 __str__ run
# __str__ run

The objects specified output format

# print打印对象时内存地址,没什么意义,此时就可以利用__str__来自定义对象打印
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):  # 重写object中的 __str__
        return f"这是要给Person对象,name:{self.name},age:{self.age}"


p = Person('jack', 10)
# 所有的类都可以转成字符串
print(p)  # 打印了
# 这是要给Person对象,name:jack,age:10
  • __del__
# del 析构函数   (__init__ 构造函数)
# 执行时机:手动删除对象时立马执行,或是程序运行结束时也会自动执行(垃圾回收机制?)
# 使用场景:当你的对象再使用过程中打开了不属于解释器的资源,例如文件,网络端口
import time


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

    def __del__(self):  # 重写object中的 __str__
        print("del run...")
        return "del run"


p = Person("jack", 20)
# del p  # 删除对象触发 __del__函数执行
# # del run...
time.sleep(2)
print("over")
# over
# del run...  # 程序结束后会把名称空间清除掉,清除时触发了 __del__,打印出 del run...

End the use of resources automatically closes the file case

class FileTool:
    # 该类用于简化文件的读写操作

    def __init__(self, path):
        self.file = open(path, 'rt', encoding='utf-8')

    def read(self):
        return self.file.read()  # rt模式不推荐直接读字节(汉字、英文字节不同),可以一行一行读

    # 执行这个函数可以确定一个函数,这个对象肯定不用了,所以就可以放心的关心文件了
    def __del__(self):
        self.file.close()


tool = FileTool("a.txt")
print(tool.read())  # 文件属于操作系统,不受垃圾回收机制管理
# aaaaaaaaaaaa

# 不知道什么不使用该对象,那就写在 __del__函数中,当其被删除时,指定关闭资源
  • __call__
# call  调用对象时自动执行
# 执行时机:在调用对象时自动执行 ---> 对象()


class A:
    # 调用对象时自动执行
    def __call__(self, *args, **kwargs):
        print("__call__ run...")
        print(args)
        print(kwargs)


a = A()

a(1, 2, a=100, c=300)  # 对象加括号调用
# __call__ run...
# (1, 2)
# {'a': 100, 'c': 300}
  • __slots__

python is a dynamic language , you can dynamically modify the properties of objects at runtime, how can store more attributes?
Need to open a larger memory area, the original property assignment last
question: if turned too much capacity (for efficiency at the expense of space), will cause the memory of the waste
solution: creating a system object is an object only to tell which properties, that is, the number of fixed properties of the object, so you can open any number of how many, to reduce wasted space (use __slots__)

import sys


class Person:
    __slots__ = ['name']  # 加了以后再添加属性就不行了,限制属性

    # def __init__(self, name, age):
    def __init__(self, name):
        self.name = name
        # self.age = age  # 未在__slots__中声明,直接报错 AttributeError: 'Person' object has no attribute 'age'


# p = Person("jck", 18)
p = Person("jck")

print(sys.getsizeof(p))  # 获取对象占用内存大小
# 56 ---> 48  ---> __slots__ 指定有哪些属性,从而节省了内存空间(没指定__slots__之前56,指定之后48)

# print(p.__dict__)  # 报错,可变字典也被省掉了(名称空间连开都不开了),AttributeError: 'Person' object has no attribute '__dict__'

This property is a class attribute for optimizing memory objects

Optimized principle: the original is not a fixed number of attributes, it becomes fixed, so the interpreter does not create a namespace to the object (it __dict__also did not), so as to achieve the effect of reducing memory overhead

Also when there is a class __slots__when the object of this class will not lead to added __slots__attributes other than defined

  • __getattr__ __setattr__ __delattr__ 及点语法原理
__getattr__ 用 .访问属性时,如果属性不存在,执行
__setattr__ 用 .设置属性时执行
__delattr__ 用del 对象.属性 删除属性时,执行

这几个函数反映了 python解释器是如何实现 . 语法的原理

__getattribute__ 该函数也是用来获取属性
在获取属性时如果存在__getattribute__则先执行该函数,如果没有拿到属性则继续调用__getattr__函数,如果拿到了则直接返回
class A:
    def __getattr__(self, item):
        print("__getattr__")
        return self.__dict__.get(item)

    def __setattr__(self, key, value):
        super().__setattr__(key, value)  # 这个不写将导致赋值不成功,得到None
        print('__setattr__')

    def __delattr__(self, item):
        print('__delattr__')
        print(item)
        self.__dict__.pop(item)


a = A()
a.name = 'jack'
# __setattr__
print(a.name)  # 这个属性存在,就没有调用 __getattr__
# jack


b = A()
b.__dict__["name"] = 'jackson'  # 通过操作__dict__ 也可以操作属性(. 语法的背后就是操作 __dict__)
print(b.name)  # 这个属性存在,就没有调用 __getattr__
# jackson

del b.name  # 触发 __delattr__
# __delattr__
# name

print(b.name)  # b没有name这个属性了,就触发了 __getattr__
# __getattr__
# None  # b没有name这个属性了
class B:
    def __setattr__(self, key, value):  # 利用了 .语法赋值改值就会触发这个函数
        self.__dict__[key] = value
        print(f"{key}:{value}")


b = B()
b.name = 'jerry'
# name:jerry
b.name = 'tom'
# name:tom
print(b.name)
# tom

b.__dict__['halo'] = 'hi'  # 直接通过操作 __dict__ 也可以完成属性的增改
print(b.halo)
# hi
  • []The realization principle __getitem__ __setitem__ __delitem__( )

Any symbol, the interpreter will be explained that a special meaning, for example. [] ()

__getitem__ 当你用中括号去获取属性时 执行
__setitem__ 当你用中括号去设置属性时 执行
__detitem__ 当你用中括号去删除属性时 执行

'''
需求:
    让一个对象支持 点语法来取值,也支持括号取值
'''


class MyDict(dict):
    def __getattr__(self, key):
        return self.get(key)
        # return self[key]  # KeyError: 'name'

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, item):
        del self[item]


# 继承 dict 可以直接用字典的一些方式
a = MyDict()
a['name'] = 'jack'
print(a['name'])
# jack

# 使用 .语法(通过实现__getattr__ 、__setattr__、__delattr__来实现)
a.name = 'sum'
print(a.name, a['name'])
# sum sum
print(a['name'])
# sum
a.name = 'jackson'
print(a.name)
# jackson
del a.name
print(a.name)
# None  # 用的是 .get 所以不会报错
  • > >= == != < <=Etc. to achieve the principles of the comparison operators (operator overloading) ( __gt__ __ge__ __eq__ __ne__ __lt__ __le__)

When we use a certain symbol, Python interpreter will be a symbol definition for the meaning of, and call the corresponding handler, when we need to custom object comparison rules, etc. can cover greater than or equal subclass Method

Case

# 自定义对象的比较
# 对象直接无法直接比较大小


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


p1 = Person('jason', 185, 18)
p2 = Person('tank', 179, 18)
# print(p1 > p2)  # TypeError: '>' not supported between instances of 'Person' and 'Person'


class Student:
    def __init__(self, name, height, age):
        self.name = name
        self.height = height
        self.age = age

    # 自定义比较规则
    def __gt__(self, other):
        print(self)
        print(other)
        print("__gt__")

        # 比身高
        # if self.height > other.height:
        #     return True
        return self.height > other.height
        # 没有返回值默认返回 None 即 False

    def __eq__(self, other):
        print("eq------")
        return self.name == other.name


stu1 = Student("jack", 180, 28)
stu2 = Student("rose", 165, 27)

print(stu1 > stu2)  # 直接报错,TypeError: '>' not supported between instances of 'Student' and 'Student'
# <__main__.Student object at 0x000001992C7C8F60>
# <__main__.Student object at 0x000001992C7C8F98>
# __gt__
# True
print(stu1 < stu2)  # 大于和小于只要实现一个即可,符号如果不同解释器会自动交换两个对象的位置
# <__main__.Student object at 0x000001992C7C8F98>
# <__main__.Student object at 0x000001992C7C8F60>
# __gt__
# False
print(stu1)
# <__main__.Student object at 0x000001992C7C8F60>
print(stu2)
# <__main__.Student object at 0x000001992C7C8F98>

Originally custom object can not be used directly greater than less than to compare, we can customize the operator to achieve, also make custom objects support comparison operators

.Other code above refers to the object of another participating Comparative

Any one of those above and below can, if different symbol interpreter automatically switching the position of two objects

Iterator protocol

Iterator: it refers __iter__and __next__objects

We can add these two methods for the object to make the object becomes an iterator

class MyIter:
    # num 传入,用来指定迭代次数
    def __init__(self, num):
        self.num = num
        self.c = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.c += 1
        if self.c <= self.num:
            return "hahha"
        raise StopIteration  # 抛出异常


for i in MyIter(3):
    print(i)
# hahha
# hahha
# hahha

Custom range function

class MyRange:
    def __init__(self, start, end, step=1):
        self.start = start - 1
        self.end = end
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        self.start += self.step
        if self.start < self.end:
            return self.start
        raise StopIteration


for i in MyRange(1, 3):
    print(i)
# 1
# 2

Context Management

Context: This concept belongs to language learning, referring to the meaning of a passage, to refer to the current scene that context

In python, the context can be understood as a section of code, a range of , for example, open files with open only valid in this context

The method involves the context of two

  • __enter__: Indicates entering the context (enter a scene of)

  • __exit__: Means exit context (leaving a scene of)

Case

class MyOpen:

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

    def __exit__(self, exc_type, exc_val, exc_tb):  # exc --> exception
        print("exit.....")
        print(exc_type, exc_val, exc_tb)


with MyOpen() as m:
    print("start...")
    # 1 + '123'
# enter....
# exit.....
# None None None

To achieve the above two methods can be used with the with statement, when executed with the statement, will be executed first __enter__, when the code is completed execution __exit__, the code or an exception is encountered will be executed immediately __exit__, passing the error message, containing errors type, wrong information, error tracking information

class MyOpen:

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

    def __exit__(self, exc_type, exc_val, exc_tb):  # exc --> exception
        print("exit.....")
        print(exc_type, exc_val, exc_tb)
        return True  # return True 可以让程序不报错


with MyOpen() as m:
    print("start...")
    1 + '123'  # TypeError: unsupported operand type(s) for +: 'int' and 'str'
# enter....
# exit.....
# None None None  # 没有报错时打印这个
# <class 'TypeError'> unsupported operand type(s) for +: 'int' and 'str' <traceback object at 0x00000283F3EE0608>  # 有错时打印这个,若__exit__ 返回为True则控制台不报错,否则控制台也会报错

important point

__enter__ 函数应该返回对象自己
__exit__ 函数可以有返回值,是一个bool类型,用于表示异常是否被处理,仅在上下文中出现异常时有用
如果为True 则意味着,异常已经被处理了
    False 异常未被处理,程序将中断报错

Guess you like

Origin www.cnblogs.com/suwanbin/p/11266025.html