zoukankan      html  css  js  c++  java
  • Python内置方法与面向对象知识点进阶系列

    Python中类的魔法方法与内置方法汇总

    之前总结的一些双下划线方法

    常见的类的内置方法 ***

    使用__getitem__与__len__实现一个可迭代/可计算长度的对象

    class Company(object):
        #魔法函数
        def __init__(self, employee_list):
            self.employee = employee_list
    
        def __getitem__(self, item):
            return self.employee[item]
        # 不写它只有__getitem__的话还是能使用len求出长度的
        def __len__(self):
            return len(self.employee)
    
    
    company = Company(["tom", "whw", "jane"])
    #
    # for i in company.employee:
    #     print(i)
    
    company1 = company[:2]
    # 调用__len__方法
    print(len(company)) # 3
    # 调用__getitem__方法
    for em in company1:
        print(em)
    """
    tom
    whw
    """

    super方法

    # 使用super函数
    # 当前的类和对象可以作为super函数的参数使用,调用返回的对象的任何方法都是
    # super函数会自动寻找他所需要的特性,直到返回一个AttributeError异常
    class Bird:
        def __init__(self):
            self.hungry=True
        def eat(self):
            if self.hungry:
                print("eat")
                self.hungry=False
            else:
                print("no")
    
    class SongBird(Bird):
        def __init__(self):
            super(SongBird,self).__init__()
            self.sound="lalala"
    
        def sing(self):
            print(self.sound)
    
    sb=SongBird()
    sb.sing() # lalala
    sb.eat() # eat
    sb.eat() # no

    使用__getitem__与super实现一个带有访问计数的list子类

    # 子类化列表,字典和字符串
    # 带有访问计数的列表
    class CounterList(list):
        def __init__(self,*args):
            super(CounterList,self).__init__(*args)
            self.counter=0
        
        def __getitem__(self, item):
            self.counter+=1
            return super(CounterList,self).__getitem__(item)
    c1=CounterList(range(10))
    print(c1) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    c1.reverse()
    print(c1) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    del c1[3:5]
    print(c1) # [9, 8, 7, 4, 3, 2, 1, 0]
    print(c1.counter) # 0
    print(c1[4]+c1[2]) # 10
    print(c1.counter) # 2

    property函数

    # property函数
    # 将访问器函数被用作参数
    class Rectangle:
        def __init__(self):
            self.width=0
            self.height=0
    
        def setSize(self,size):
            self.width,self.height=size
    
        def getSize(self):
            return self.width,self.height
        
        size=property(getSize,setSize)
    
    r=Rectangle()
    r.width=10
    r.height=5
    print(r.size) # (10, 5)
    r.size=150,100
    print(r.width) # 150

    静态成员方法与类成员方法

    # 静态成员方法和类成员方法
    class MyClass:
        def smeth():
            print("This is a static method")
        smeth=staticmethod(smeth)
    
        def cmeth(cls):
            print("This is a class methon of'",cls)
            cmeth=classmethod(cls.cmeth)
    
    #装饰器
    class MyClass1:
        @staticmethod
        def smeth():
            print("This is a static method")
    
        @classmethod
        def cmeth(cls):
            print("This is a class method",cls)
    
    cla=MyClass1()
    cla.smeth() # This is a static method
    cla.cmeth() # This is a class method <class '__main__.MyClass1'>

    __getattr__与__setattr__

    #__getattr__、__setattr__
    class Rectangle:
        def __init__(self):
            self.width=0
            self.height=0
    
        def __setattr__(self, key, value):
            if key=='size':
                self.width,self.height=value
            else:
                self.__dict__[key]=value
    
        def __getattr__(self, item):
            if item=='size':
                return self.width,self.height
            else:
                raise AttributeError
    
    d=Rectangle()
    d.height=10
    d.width=10
    print(d.__getattr__(item='size')) # (10, 10)
    d.__setattr__(key='size',value=(100,200))
    print(d.height) # 200

    __iter__与__next__方法简介

    一个实现了__iter__方法的对象是可以迭代的,一个实现了__next__方法的对象则是迭代器!

    # 迭代时候最好不要用列表,如果有很多值,列表会占用太多的内存
    # 使用迭代器更通用,更简单,更优雅。
    class Fibs:
        def __init__(self):
            self.a=0
            self.b=1
    
        def __next__(self):
            self.a,self.b=self.b,self.a+self.b
            return self.a
    
        def __iter__(self):
            return self
    
    # 一个实现了__iter__方法的对象是可以迭代的,一个实现了__next__方法的对象则是迭代器
    fibs=Fibs()
    
    for f in fibs:
        if f>1000:
            print(">>>",f)
            break
    
    #内建函数iter可以从可迭代的对象中获得迭代器
    it=iter([1,2,3])
    print(next(it))
    print(next(it))
    print(next(it))
    """
    >>> 1597
    1
    2
    3
    """

    从迭代器得到序列

    # 除了再迭代器和可迭代对象向上进行迭代,还可以把他们转换成序列
    class TestIterator:
        value=0
    
        def __next__(self):
            self.value+=1
            if self.value>10:raise StopIteration
            return self.value
    
        def __iter__(self):
            return self
    
    t1=TestIterator()
    print(list(t1)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    八皇后问题

    # 八皇后问题
    # 首先寻找冲突
    # 找到一种没有冲突的位置(没有皇后会被其他的皇后吃掉)
    def conflict(state,nextX):
        nextY=len(state)
        for i in range(nextY):
            if abs(state[i]-nextX) in (0,nextY-i):
                return True
        return False
    
    # 基本情况
    # 周后一个皇后能够根据其他皇后的位置生成他自己能占据的位置
    def queens(num,state):
        if len(state)==num-1:
            for pos in range(num):
                if not conflict(state,pos):
                    yield pos
    
    
    # 需要递归的情况
    # 递归函数需要假定所有的来自低层的结果都是正确的
    # 假定将位置信息作为一个元组返回,需要修改基本情况也返回一个元组
    # 这样一来,程序会从前面的皇后得到包含位置的元组信息,并且为后面的皇后提供当前皇后的每种合法位置信息
    def queens2(num=8,state=()):
            for pos in range(num):
                if not conflict(state,pos):
                    if len(state)==num-1:
                        yield (pos,)
                    else:
                        for result in queens2(num,state+(pos,)):
                            yield (pos,)+result
    
    # 打包输出
    def prettyprint(solution):
        def line(pos,length=len(solution)):
            return '. '*(pos)+'X '+'. '*(length-pos-1)
        for pos in solution:
            print(line(pos))
    
    import random
    prettyprint(random.choice(list(queens2(100))))

    Python中类的归一化设计

    类的归一化设计abc模块与自定义归一化设计

    isinstance与type

    聊一聊isinstance与type

    类与对象进阶

    类与实例的查找顺序--mro查找 

    #新式类
    class D:
        pass
    
    class E:
        pass
    
    class C(E):
        pass
    
    class B(D):
        pass
    
    class A(B, C):
        pass
    
    print(A.__mro__)
    """
    (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
    """

    访问类的私有属性

    from chapter04.class_method import Date
    class User:
        def __init__(self, birthday):
            self.__birthday = birthday
    
        def get_age(self):
            #返回年龄
            return 2018 - self.__birthday.year
    
    
    if __name__ == "__main__":
        user = User(Date(1990,2,1))
        # 访问私有属性
        print(user._Student__birthday)
        print(user.get_age())

    Python对象的自省机制:__dict__ 与 dir()

    —— 自省是通过一定的机制查询到对象的内部结构!

    class Person:
        # 类属性
        name = "user"
    
    class Student(Person):
        def __init__(self, scool_name):
            self.school_name = scool_name
    
    if __name__ == "__main__":
        user = Student("一了拉面")
    
        ### __dict__
        # 通过__dict__查询属性
        print(user.__dict__)
        # 设置
        user.__dict__["school_addr"] = "火之国"
        print(user.school_addr) # 火之国
        print(Person.__dict__) # {'__module__': '__main__', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
        print(user.name) # user
    
        ### dir方法
        a = [1,2]
        print(dir(a))
        # ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

    深入理解super函数 ***

    super的继承顺序其实遵循mro的顺序(Python3中)!

    from threading import Thread
    
    class MyThread(Thread):
        def __init__(self, name, user):
            self.user = user
            super().__init__(name=name)
    
    
    class A:
        def __init__(self):
            print ("A")
    
    class B(A):
        def __init__(self):
            print ("B")
            super().__init__()
    
    class C(A):
        def __init__(self):
            print ("C")
            super().__init__()
            
    class D(B, C):
        def __init__(self):
            print ("D")
            super(D, self).__init__()
    
    if __name__ == "__main__":
        print("mro>>>>>>",D.__mro__) 
        """
        mro>>>>>> (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
        """
        d = D()
        """
        D
        B
        C
        A
        """

    DRF的mixin模式

    # mixin模式特点
    # 1. Mixin类功能单一
    # 2. 不和基类关联,可以和任意基类组合, 基类可以不和mixin关联就能初始化成功
    # 3. 在mixin中不要使用super这种用法

    上下文管理 ***

    with上下文管理

    Python中的序列类之实现可切片对象 ***

    列表切片的说明

    # 模式[start:end:step]
    """
        其中,第一个数字start表示切片开始位置,默认为0;
        第二个数字end表示切片截止(但不包含)位置(默认为列表长度);
        第三个数字step表示切片的步长(默认为1)。
        当start为0时可以省略,当end为列表长度时可以省略,
        当step为1时可以省略,并且省略步长时可以同时省略最后一个冒号。
        另外,当step为负整数时,表示反向切片,这时start应该比end的值要大才行。
    """
    aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
    print (aList[::])  # 返回包含原列表中所有元素的新列表
    print (aList[::-1])  # 返回包含原列表中所有元素的逆序列表
    print (aList[::2])  # 隔一个取一个,获取偶数位置的元素
    print (aList[1::2])  # 隔一个取一个,获取奇数位置的元素
    print (aList[3:6])  # 指定切片的开始和结束位置
    aList[0:100]  # 切片结束位置大于列表长度时,从列表尾部截断
    aList[100:]  # 切片开始位置大于列表长度时,返回空列表
    
    aList[len(aList):] = [9]  # 在列表尾部增加元素
    aList[:0] = [1, 2]  # 在列表头部插入元素
    aList[3:3] = [4]  # 在列表中间位置插入元素
    aList[:3] = [1, 2]  # 替换列表元素,等号两边的列表长度相等
    aList[3:] = [4, 5, 6]  # 等号两边的列表长度也可以不相等
    aList[::2] = [0] * 3  # 隔一个修改一个
    print (aList)
    aList[::2] = ['a', 'b', 'c']  # 隔一个修改一个
    print (aList)
    #aList[::2] = [1,2]  # 左侧切片不连续,等号两边列表长度必须相等
    aList[:3] = []  # 删除列表中前3个元素
    print (aList)
    del aList[:3]  # 切片元素连续
    print (aList)
    del aList[::2]  # 切片元素不连续,隔一个删一个

    自定义切片对象

    import numbers
    class Group: #支持切片操作 def __init__(self, group_name, company_name, staffs): self.group_name = group_name self.company_name = company_name self.staffs = staffs def __reversed__(self): self.staffs.reverse() def __getitem__(self, item): # 关键! cls = type(self) if isinstance(item, slice): return cls(group_name=self.group_name, company_name=self.company_name, staffs=self.staffs[item]) elif isinstance(item, numbers.Integral): return cls(group_name=self.group_name, company_name=self.company_name, staffs=[self.staffs[item]]) def __len__(self): return len(self.staffs) def __iter__(self): return iter(self.staffs) def __contains__(self, item): if item in self.staffs: return True else: return False staffs = ["whw1", "naruto", "whw2", "whw3"] group = Group(company_name="naruto", group_name="user", staffs=staffs) reversed(group) for user in group: print(user)

    Python中的序列类之bisect管理排序序列 *****

    —— 内部使用二分查找,业务中如果维护一个已排序的序列,尽量使用bisect!这样不用二次排序了!!! 

    import bisect
    from collections import deque
    
    # 用来处理已排序的序列,用来维持已排序的序列, 升序
    inter_list = deque()
    
    bisect.insort(inter_list, 3)
    bisect.insort(inter_list, 2)
    bisect.insort(inter_list, 5)
    bisect.insort(inter_list, 1)
    bisect.insort(inter_list, 6)
    # 已经排好序了
    print(inter_list) # deque([1, 2, 3, 5, 6])
    
    # 如果插入4的话在哪个位置插入
    print(bisect.bisect_left(inter_list, 4)) # 3
    print(bisect.bisect_right(inter_list, 4)) # 3 

    什么时候不用list *

    array

    数组 —— 性能非常高。

    # array, deque
    # 数组
    import array
    a=list()
    print(a)
    # array和list的一个重要区别, array只能存放指定的数据类型
    my_array = array.array("i")
    # int
    my_array.append(1)
    my_array.append(7)
    print(my_array)

    deque

    —— deque是线程安全的!

    from collections import deque
    
    # 双端队列——支持列表的操作!
    d = deque([1,2,3,4])
    print(d)#deque([1, 2, 3, 4])
    # 右边添加
    d.append(5)#deque([1, 2, 3, 4, 5])
    print(d)
    # 左边添加
    d.appendleft(6)
    print(d)#deque([6, 1, 2, 3, 4, 5])
    # pop——不能给参数
    print(d.pop())#5
    print(d.popleft())#6
    print(d)#deque([1, 2, 3, 4])

    列表当作默认参数的一个坑 ***

    class Company:
        def __init__(self, name, staffs=[]):
            self.name = name
            self.staffs = staffs
        def add(self, staff_name):
            self.staffs.append(staff_name)
        def remove(self, staff_name):
            self.staffs.remove(staff_name)
    
    if __name__ == "__main__":
        # 初始化的时候指定staffs列表的话,用传入的这个列表
        com1 = Company("com1", ["whw1", "whw2"])
        com1.add("whw3")
        com1.remove("whw1")
        print(com1.staffs) # ['whw2', 'whw3']
    
        # 初始化的时候不指定staffs的话,用默认的哪个列表!—— 可能会与其他不指定staffs的对象用同一个列表!
        com2 = Company("com2")
        com2.add("whw")
        print(com2.staffs) # ['whw']
    
    
        print("default>>",Company.__init__.__defaults__) # default>> (['whw'],)
        
        com3 = Company("com3")
        com3.add("whw5")
        print (com2.staffs) # ['whw', 'whw5']
        print (com3.staffs) # ['whw', 'whw5']
        print (com2.staffs is com3.staffs) # True

    __new__与__init__区别简介

    class User:
        def __new__(cls, *args, **kwargs):
            print (" in new ")
            return super().__new__(cls)
        def __init__(self, name):
            print (" in init")
            pass
    a = int()
    
    # new 是用来控制对象的生成过程, 在对象生成之前 # init是用来完善对象的 # 如果new方法不返回对象, 则不会调用init函数

    if __name__ == "__main__": user = User(name="whw")

    property装饰器 ***

    1. get和set的方法名称都要一样(age)
    2. set方法返回的属性前面加个"_"
    3. @property是针对get方法
    4. @age.setter是针对set方法,是@property本身又创建了另一个装饰器
    5. 直接可以这样stu.age=10对象名.方法名进行赋值,
    6. 只定义getter方法,不定义setter方法是一个只读属性

    from datetime import date, datetime
    
    class User:
        def __init__(self, name, birthday):
            self.name = name
            self.birthday = birthday
            self._age = 0
    
        # 加上property装饰器可以像访问属性那样获取数据
        @property
        def age(self):
            return datetime.now().year - self.birthday.year
    
        # 设置
        @age.setter
        def age(self,value):
            if isinstance(value,int):
                if 0<value<120:
                    self._age=valueelse:
                print("请输入合法的年龄")
    
    if __name__ == "__main__":
        user = User("whw", date(year=1995, month=2, day=22))
        user.age = 30
        print (user._age) # 30
        print(user.age) # 25

    __getattr__与__getattribute__方法

    1、__getattr__:查找不到属性的时候进入这里(不考虑继承)。

    2、__getattribute__:执行查找,无条件进入该魔法函数,即使查找的属性不存在(不考虑继承)。

    #__getattr__, __getattribute__
    #__getattr__ 就是在查找不到属性的时候调用
    
    class User:
        def __init__(self,info={}):
            self.info = info
    
        #  查找不到属性的时候进入这里
        def __getattr__(self, item):
            return self.info[item]
    
        # 执行查找, 无条件进入该魔法函数, 即使所查找的属性不存在
        # def __getattribute__(self, item):
        #     return "火之影"
    
    if __name__ == "__main__":
        user = User(info={"company_name":"china", "name":"whw"})
    
        ### 如果不注释__getattribute__会打印:
        """
        火之影
        火之影
        """
        print(user.company_name)
        print(user.my_name)
    
        ### 如果注释掉_getattribute__会:
        """
        1.有company_name会打印china
        2.没有my_name会报错
        """

    __getattr__、__getattribute__与继承的关系

    结论:优先使用双下划线的方法中的值!

    #__getattr__, __getattribute__
    #__getattr__ 就是在查找不到属性的时候调用
    
    class UserBase:
        def __init__(self,age):
            self.age = age
            self.my_name = "Base"
    
    
    class User(UserBase):
        def __init__(self,info={}):
            self.info = info
            super().__init__(info)
    
        #  查找不到属性的时候进入这里
        def __getattr__(self, item):
            return self.info[item]
    
        # 执行查找, 无条件进入该魔法函数, 即使所查找的属性不存在
        # def __getattribute__(self, item):
        #     return "火之影"
    
    if __name__ == "__main__":
        user = User(info={"company_name":"china", "name":"whw"})
    
        ### 如果不注释__getattribute__会打印:———— 优先会调用自己的__getattribute__方法返回的数据!
        """
        火之影
        火之影
        """
        print(user.company_name)
        print(user.my_name)
    
        ### 如果注释掉__getattribute__会打印:
        """
        china
        Base
        """

    Python属性描述符 **

    个人Python描述符的博客

    Python中的描述符

    使用属性描述符校验

    上面介绍property的时候提到的age在输入的时候需要校验, 实现是通过property的setter, 但是如果很多输入字段那么就要写很多重复的代码.这里就要用到属性描述符。

    import numbers
    
    
    class IntField:
        # 数据描述符
        def __get__(self, instance, owner):
            return self.value
    
        def __set__(self, instance, value):
            if not isinstance(value, numbers.Integral):
                raise ValueError("int value need")
            if value < 0:
                raise ValueError("positive value need")
            self.value = value
    
        def __delete__(self, instance):
            pass
    
    
    class User:
        age = IntField()
    
    
    if __name__ == "__main__":
        user = User()
        user.age = 30           # 进入数据描述符的__set__
        setattr(user, 'age',18) # 进入数据描述符的__get__
        print(user.age)         # 进入数据描述符的__get__
        user.__dict__["age"] = 18
        print(user.__dict__["age"])
        user.__dict__["age"] = 18
        print(user.age)
        """
        输出全是18
        """

    简单的例子说明

    class User:
        age = 1
    
    if __name__ == "__main__":
        user = User()
        user.name = 30         # 保存在user对象的内存中
        print(user.name)       # 从user对象的内存中去取
        user.age = 30          # 保存在user对象的内存中, 不影响类的内存中的值
        print(user.age)        # 进入数据描述符的__get__
        user.__dict__["age"] = 18
        print(user.__dict__["age"])
        print (user.__dict__)
        """
        30
        30
        18
        {'name': 30, 'age': 18}
        """

    Python中属性的查找顺序 *****

    对象user的属性age的查找顺序(user.age)

    • 如果user是某个类的实例,那么user.age —— 以及等价的getattr(user,’age’)。
    • 首先调用__getattribute__, 如果在__getattribute__找不到属性就会抛出AttributeError。
    • 如果类定义了__getattr__方法,在抛出AttributeError的时候就会调用到__getattr__。
    • 而对于描述符__get__的调用,则是发生在__getattribute__内部的。

    完整的查找顺序如下 ***

    • 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor,那么调用其__get__方法
    • 如果“age”出现在user(对象)的__dict__中, 那么直接返回 obj.__dict__[‘age’]
    • 如果“age”出现在User(类)或其基类的__dict__中:
    1. 如果age是non-data descriptor,那么调用其__get__方法
    2. 返回__dict__[‘age’]
    • 如果User有__getattr__方法,调用__getattr__方法,否则抛出AttributeError
    1. 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类.__dict__里的
    2. 对象.__dict__中存储了一些self.xxx的一些东西
  • 相关阅读:
    XAML实例教程系列
    XAML实例教程系列
    XAML实例教程系列
    正则表达式 修改流程 过程是崎岖的
    Codeforces Round #379 (Div. 2) 解题报告
    (DFS)codevs1004-四子连棋
    (BFS)poj2935-Basic Wall Maze
    (BFS)poj1465-Multiple
    (BFS)uva2554-Snakes & Ladders
    (BFS)hdoj2377-Bus Pass
  • 原文地址:https://www.cnblogs.com/paulwhw/p/12940991.html
Copyright © 2011-2022 走看看