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的一些东西

 

posted on 2020-05-23 08:44  江湖乄夜雨  阅读(415)  评论(0编辑  收藏  举报