<jupyter_start><jupyter_text>魔法方法魔法方法总是被双下划线包围，例如`__init__`。魔法方法是面向对象的 Python 的一切，如果你不知道魔法方法，说明你还没能意识到面向对象的 Python 的强大。魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用。魔法方法的第一个参数应为`cls`（类方法） 或者`self`（实例方法）。- `cls`：代表一个类的名称- `self`：代表一个实例对象的名称--- 1. 基本的魔法方法- `__init__(self[, ...])` 构造器，当一个实例被创建的时候调用的初始化方法<jupyter_code>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<jupyter_output><empty_output><jupyter_text>- `__new__(cls[, ...])` 在一个对象实例化的时候所调用的第一个方法，在调用`__init__`初始化前，先调用`__new__`。    - `__new__`至少要有一个参数`cls`，代表要实例化的类，此参数在实例化时由 Python 解释器自动提供，后面的参数直接传递给`__init__`。    - `__new__`对当前类进行了实例化，并将实例返回，传给`__init__`的`self`。但是，执行了`__new__`，并不一定会进入`__init__`，只有`__new__`返回了，当前类`cls`的实例，当前类的`__init__`才会进入。<jupyter_code>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)
# 结果：
# 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)  # 改动了cls变为A


b = B(10)
# 结果：
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>
```<jupyter_output><empty_output><jupyter_text>- 若`__new__`没有正确返回当前类`cls`的实例，那`__init__`是不会被调用的，即使是父类的实例也不行，将没有`__init__`被调用。<jupyter_code>class Earth:
    pass


a = Earth()
print(id(a))  # 260728291456
b = Earth()
print(id(b))  # 260728291624

class Earth:
    __instance = None  # 定义一个类属性做判断

    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))  # 512320401648
b = Earth()
print(id(b))  # 512320401648<jupyter_output><empty_output><jupyter_text>- `__new__`方法主要是当你继承一些不可变的 class 时（比如`int, str, tuple`）， 提供给你一个自定义这些类的实例化过程的途径。<jupyter_code>class CapStr(str):
    def __new__(cls, string):
        string = string.upper()
        return str.__new__(cls, string)


a = CapStr("i love lsgogroup")
print(a)  # I LOVE LSGOGROUP<jupyter_output><empty_output><jupyter_text>- `__del__(self)` 析构器，当一个对象将要被系统回收之时调用的方法。> Python 采用自动引用计数（ARC）方式来回收对象所占用的空间，当程序中有一个变量引用该 Python 对象时，Python 会自动保证该对象引用计数为 1；当程序中有两个变量引用该 Python 对象时，Python 会自动保证该对象引用计数为 2，依此类推，如果一个对象的引用计数变成了 0，则说明程序中不再有变量引用该对象，表明程序不再需要该对象，因此 Python 就会回收该对象。>> 大部分时候，Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况，比如对象 a 持有一个实例变量引用对象 b，而对象 b 又持有一个实例变量引用对象 a，此时两个对象的引用计数都是 1，而实际上程序已经不再有变量引用它们，系统应该回收它们，此时 Python 的垃圾回收器就可能没那么快，要等专门的循环垃圾回收器（Cyclic Garbage Collector）来检测并回收这种引用循环。<jupyter_code>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__<jupyter_output><empty_output><jupyter_text>- `__str__(self)`:    - 当你打印一个对象的时候，触发`__str__`    - 当你使用`%s`格式化的时候，触发`__str__`    - `str`强转数据类型的时候，触发`__str__`- `__repr__(self)`：    - `repr`是`str`的备胎    - 有`__str__`的时候执行`__str__`,没有实现`__str__`的时候，执行`__repr__`    - `repr(obj)`内置函数对应的结果是`__repr__`的返回值    - 当你使用`%r`格式化的时候 触发`__repr__`<jupyter_code>class Cat:
    """定义一个猫类"""

    def __init__(self, new_name, new_age):
        """在创建完对象之后 会自动调用, 它完成对象的初始化的功能"""
        self.name = new_name
        self.age = new_age

    def __str__(self):
        """返回一个对象的描述信息"""
        return "名字是:%s , 年龄是:%d" % (self.name, self.age)
        
    def __repr__(self):
        """返回一个对象的描述信息"""
        return "Cat:(%s,%d)" % (self.name, self.age)

    def eat(self):
        print("%s在吃鱼...." % self.name)

    def drink(self):
        print("%s在喝可乐..." % self.name)

    def introduce(self):
        print("名字是:%s, 年龄是:%d" % (self.name, self.age))
# 创建了一个对象
tom = Cat("汤姆", 30)
print(tom)  # 名字是:汤姆 , 年龄是:30
print(str(tom)) # 名字是:汤姆 , 年龄是:30
print(repr(tom))  # Cat:(汤姆,30)
tom.eat()  # 汤姆在吃鱼....
tom.introduce()  # 名字是:汤姆, 年龄是:30<jupyter_output><empty_output><jupyter_text>`__str__(self)` 的返回结果可读性强。也就是说，`__str__` 的意义是得到便于人们阅读的信息，就像下面的 '2019-10-11' 一样。`__repr__(self)` 的返回结果应更准确。怎么说，`__repr__` 存在的目的在于调试，便于开发者使用。<jupyter_code>import datetime

today = datetime.date.today()
print(str(today))  # 2019-10-11
print(repr(today))  # datetime.date(2019, 10, 11)
print('%s' %today)  # 2019-10-11
print('%r' %today)  # datetime.date(2019, 10, 11)<jupyter_output><empty_output><jupyter_text>6. 属性访问- `__getattr__(self, name)`: 定义当用户试图获取一个不存在的属性时的行为。- `__getattribute__(self, name)`：定义当该类的属性被访问时的行为（先调用该方法，查看是否存在该属性，若不存在，接着去调用`__getattr__`）。- `__setattr__(self, name, value)`：定义当一个属性被设置时的行为。- `__delattr__(self, name)`：定义当一个属性被删除时的行为。<jupyter_code>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__<jupyter_output><empty_output><jupyter_text>扩展参考：- [技术图文：Python魔法方法之属性访问详解](https://mp.weixin.qq.com/s?__biz=MzIyNDA1NjA1NQ==&mid=2651011398&idx=1&sn=412562db40c2c4efacc3d75b64990a8f&chksm=f3e35edec494d7c8b3baffdc2806332ea358030d7c55cc9df27da19e5cae7c51917beb057dfd&token=523711417&lang=zh_CNrd)  7. 描述符描述符就是将某种特殊类型的类的实例指派给另一个类的属性。- `__get__(self, instance, owner)`用于访问属性，它返回属性的值。- `__set__(self, instance, value)`将在属性分配操作中调用，不返回任何内容。- `__del__(self, instance)`控制删除操作，不返回任何内容。<jupyter_code>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><jupyter_output><empty_output><jupyter_text>扩展参考：- [技术图文：什么是Python的描述符？](https://mp.weixin.qq.com/s?__biz=MzIyNDA1NjA1NQ==&mid=2651011392&idx=1&sn=8dc65617504e909862113d4d1f36c2cb&chksm=f3e35ed8c494d7ce55cab936a1477998ccdf6eb865553a7174da44dcfb63b3807d3c4beb9104&token=<PASSWORD>&lang=zh_CNrd)  8. 定制序列协议（Protocols）与其它编程语言中的接口很相似，它规定你哪些方法必须要定义。然而，在 Python 中的协议就显得不那么正式。事实上，在 Python 中，协议更像是一种指南。**容器类型的协议**- 如果说你希望定制的容器是不可变的话，你只需要定义`__len__()`和`__getitem__()`方法。- 如果你希望定制的容器是可变的话，除了`__len__()`和`__getitem__()`方法，你还需要定义`__setitem__()`和`__delitem__()`两个方法。【例子】编写一个不可改变的自定义列表，要求记录列表中每个元素被访问的次数。<jupyter_code>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}<jupyter_output><empty_output><jupyter_text>- `__len__(self)`定义当被`len()`调用时的行为（返回容器中元素的个数）。- `__getitem__(self, key)`定义获取容器中元素的行为，相当于`self[key]`。- `__setitem__(self, key, value)`定义设置容器中指定元素的行为，相当于`self[key] = value`。- `__delitem__(self, key)`定义删除容器中指定元素的行为，相当于`del self[key]`。 【例子】编写一个可改变的自定义列表，要求记录列表中每个元素被访问的次数。<jupyter_code>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}<jupyter_output><empty_output><jupyter_text>9. 迭代器- 迭代是 Python 最强大的功能之一，是访问**集合元素**的一种方式。- 迭代器是一个可以记住遍历的位置的对象。- 迭代器对象从集合的第一个元素开始访问，直到所有的元素被访问完结束。- 迭代器只能**往前不会后退**。- **字符串，列表或元组对象**都可用于创建迭代器：<jupyter_code>string = 'lsgogroup'
for c in string:
    print(c)

'''
l
s
g
o
g
r
o
u
p
'''
links = {'B': '百度', 'A': '阿里', 'T': '腾讯'}
for each in links:
    print('%s -> %s' % (each, links[each]))
    
'''
B -> 百度
A -> 阿里
T -> 腾讯
'''

for each in iter(links):
    print('%s -> %s' % (each, links[each]))<jupyter_output><empty_output><jupyter_text>- 迭代器有两个基本的方法：`iter()` 和 `next()`。- `iter(object)` 函数用来生成迭代器。- `next(iterator[, default])` 返回迭代器的下一个项目。- `iterator` -- 可迭代对象- `default` -- 可选，用于设置在没有下一个元素时返回该默认值，如果不设置，又没有下一个元素则会触发 `StopIteration` 异常。<jupyter_code>links = {'B': '百度', 'A': '阿里', 'T': '腾讯'}
it = iter(links)
print(next(it))  # B
print(next(it))  # A
print(next(it))  # T
print(next(it))  # StopIteration


it = iter(links)
while True:
    try:
        each = next(it)
    except StopIteration:
        break
    print(each)

# B
# A
# T<jupyter_output><empty_output><jupyter_text>把一个类作为一个迭代器使用需要在类中实现两个魔法方法 `__iter__()` 与 `__next__()` 。- `__iter__(self)`定义当迭代容器中的元素的行为，返回一个特殊的迭代器对象， 这个迭代器对象实现了 `__next__()` 方法并通过 `StopIteration` 异常标识迭代的完成。- `__next__()` 返回下一个迭代器对象。- `StopIteration` 异常用于标识迭代的完成，防止出现无限循环的情况，在 `__next__()` 方法中我们可以设置在完成指定循环次数后触发 `StopIteration` 异常来结束迭代。<jupyter_code>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<jupyter_output><empty_output><jupyter_text>10. 生成器- 在 Python 中，使用了 `yield` 的函数被称为生成器（generator）。- 跟普通函数不同的是，生成器是一个返回迭代器的函数，只能用于迭代操作，更简单点理解生成器就是一个迭代器。- 在调用生成器运行的过程中，每次遇到 `yield` 时函数会暂停并保存当前所有的运行信息，返回 `yield` 的值, 并在下一次执行 `next()` 方法时从当前位置继续运行。- 调用一个生成器函数，返回的是一个迭代器对象。<jupyter_code>def myGen():
    print('生成器执行！')
    yield 1
    yield 2


myG = myGen()
print(next(myG))  
# 生成器执行！
# 1

print(next(myG))  # 2
print(next(myG))  # StopIteration

myG = myGen()
for each in myG:
    print(each)

'''
生成器执行！
1
2
'''
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<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15606936416184972, 'fraction_numerical': 0.04401321222130471, 'mean_word_length': 4.120930232558139, 'pattern_counts': {'":': 0, '<': 73, '<?xml version=': 0, '>': 84, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '201395', 'license': 'MIT', 'n_tokens_mistral': 6621, 'n_tokens_neox': 6517, 'n_words': 914, 'path': 'Task7/Task7_2.ipynb', 'repo_name': 'ovechou/pythonBasic', 'size': '12109'}