Skip to content

Commit

Permalink
所有基础文章修改完毕
Browse files Browse the repository at this point in the history
  • Loading branch information
walter201230 committed Oct 14, 2019
1 parent 95ad977 commit d7a75da
Show file tree
Hide file tree
Showing 48 changed files with 387 additions and 145 deletions.
12 changes: 9 additions & 3 deletions Article/python10/1.md → Article/PythonBasis/python10/1.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# 一、Python 的 Magic Method #

在 Python 中,所有以 "__" 双下划线包起来的方法,都统称为"魔术方法"。比如我们接触最多的 `__init__` 。魔术方法有什么作用呢?
在 Python 中,所有以 "__" 双下划线包起来的方法,都统称为"魔术方法"。比如我们接触最多的 `__init__`

魔术方法有什么作用呢?

使用这些魔术方法,我们可以构造出优美的代码,将复杂的逻辑封装成简单的方法。

Expand All @@ -22,6 +24,10 @@ if __name__ == '__main__':

输出的结果:

![Python 类的魔术方法](http://p1ceh5usj.bkt.clouddn.com/Python%20%E7%B1%BB%E7%9A%84%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95.png)
```
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
```

可以看到,一个类的魔术方法还是挺多的,不过我们只需要了解一些常见和常用的魔术方法就好了。


可以看到,一个类的魔术方法还是挺多的,截图也没有截全,不过我们只需要了解一些常见和常用的魔术方法就好了。
12 changes: 9 additions & 3 deletions Article/python10/2.md → Article/PythonBasis/python10/2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# 二、构造(`__new__`)和初始化(`__init__`) #

通过上一篇的内容,我们已经知道定义一个类时,我们经常会通过 `__init__(self)` 的方法在实例化对象的时候,对属性进行设置。比如下面的例子:
通过之前的学习,我们已经知道定义一个类时,我们经常会通过 `__init__(self)` 的方法在实例化对象的时候,对属性进行设置。

比如下面的例子:

```python
#!/usr/bin/env python3
Expand All @@ -14,9 +16,11 @@ class User(object):
user=User('两点水',23)
```

实际上,创建一个类的过程是分为两步的,一步是创建类的对象,还有一步就是对类进行初始化。`__new__` 是用来创建类并返回这个类的实例, 而`__init__` 只是将传入的参数来初始化该实例.`__new__` 在创建一个实例的过程中必定会被调用,但 `__init__` 就不一定,比如通过pickle.load 的方式反序列化一个实例时就不会调用 `__init__` 方法。
实际上,创建一个类的过程是分为两步的,一步是创建类的对象,还有一步就是对类进行初始化。

`__new__` 是用来创建类并返回这个类的实例, 而`__init__` 只是将传入的参数来初始化该实例.`__new__` 在创建一个实例的过程中必定会被调用,但 `__init__` 就不一定,比如通过 pickle.load 的方式反序列化一个实例时就不会调用 `__init__` 方法。

![Python类创建的过程](http://upload-images.jianshu.io/upload_images/2136918-a2b39b078cc81841?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python%E7%B1%BB%E5%88%9B%E5%BB%BA%E7%9A%84%E8%BF%87%E7%A8%8B.png)

`def __new__(cls)` 是在 `def __init__(self)` 方法之前调用的,作用是返回一个实例对象。还有一点需要注意的是:`__new__` 方法总是需要返回该类的一个实例,而 `__init__` 不能返回除了 `None` 的任何值

Expand Down Expand Up @@ -57,3 +61,5 @@ if __name__ == '__main__':
其实在实际开发中,很少会用到 `__new__` 方法,除非你希望能够控制类的创建。通常讲到 `__new__` ,都是牵扯到 `metaclass`(元类)的。

当然当一个对象的生命周期结束的时候,析构函数 `__del__` 方法会被调用。但是这个方法是 Python 自己对对象进行垃圾回收的。


File renamed without changes.
18 changes: 15 additions & 3 deletions Article/python10/4.md → Article/PythonBasis/python10/4.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# 四、对象的描述器 #

一般来说,一个描述器是一个有“绑定行为”的对象属性 (object attribute),它的访问控制被描述器协议方法重写。这些方法是 `__get__()`, `__set__()` , 和 `__delete__()` 。有这些方法的对象叫做描述器。
一般来说,一个描述器是一个有“绑定行为”的对象属性 (object attribute),它的访问控制被描述器协议方法重写。

默认对属性的访问控制是从对象的字典里面 (`__dict__`) 中获取 (get) , 设置 (set) 和删除 (delete) 。举例来说, `a.x` 的查找顺序是, `a.__dict__['x']` , 然后 `type(a).__dict__['x']` , 然后找 `type(a)` 的父类 ( 不包括元类 (metaclass) ).如果查找到的值是一个描述器, Python 就会调用描述器的方法来重写默认的控制行为。这个重写发生在这个查找环节的哪里取决于定义了哪个描述器方法。注意, 只有在新式类中时描述器才会起作用。在之前的篇节中已经提到新式类和旧式类的,有兴趣可以查看之前的篇节来看看,至于新式类最大的特点就是所有类都继承自 type 或者 object 的类
这些方法是 `__get__()`, `__set__()` , `__delete__()`

在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。在 Django 的 ORM 中,models.Model中的 InterField 等字段, 就是通过描述器来实现功能的。
有这些方法的对象叫做描述器。

默认对属性的访问控制是从对象的字典里面 (`__dict__`) 中获取 (get) , 设置 (set) 和删除 (delete) 。

举例来说, `a.x` 的查找顺序是, `a.__dict__['x']` , 然后 `type(a).__dict__['x']` , 然后找 `type(a)` 的父类 ( 不包括元类 (metaclass) ).如果查找到的值是一个描述器, Python 就会调用描述器的方法来重写默认的控制行为。

这个重写发生在这个查找环节的哪里取决于定义了哪个描述器方法。

注意, 只有在新式类中时描述器才会起作用。在之前的篇节中已经提到新式类和旧式类的,有兴趣可以查看之前的篇节来看看,至于新式类最大的特点就是所有类都继承自 type 或者 object 的类。

在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。在 Django 的 ORM 中,models.Model 中的 InterField 等字段, 就是通过描述器来实现功能的。

我们先看下下面的例子:

Expand Down Expand Up @@ -129,3 +139,5 @@ if __name__ == '__main__':
我们只是修改了 meter ,并且将其赋值成为 int ,但 foot 也修改了。这是 `__set__` 发挥了作用.

描述器对象 (Meter、Foot) 不能独立存在, 它需要被另一个所有者类 (Distance) 所持有。描述器对象可以访问到其拥有者实例的属性,比如例子中 Foot 的 `instance.meter`


10 changes: 8 additions & 2 deletions Article/python10/5.md → Article/PythonBasis/python10/5.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# 五、自定义容器(Container) #

经过之前编章的介绍,我们知道在 Python 中,常见的容器类型有: dict, tuple, list, string。其中也提到过可容器和不可变容器的概念。其中 tuple, string 是不可变容器,dict, list 是可变容器。 可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。当然具体的介绍,可以看回之前的文章,有图文介绍。
经过之前编章的介绍,我们知道在 Python 中,常见的容器类型有: dict, tuple, list, string。其中也提到过可容器和不可变容器的概念。其中 tuple, string 是不可变容器,dict, list 是可变容器。

那么这里先提出一个问题,这些数据结构就够我们开发使用吗?不够的时候,或者说有些特殊的需求不能单单只使用这些基本的容器解决的时候,该怎么办呢?
可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。当然具体的介绍,可以看回之前的文章,有图文介绍。

那么这里先提出一个问题,这些数据结构就够我们开发使用吗?

不够的时候,或者说有些特殊的需求不能单单只使用这些基本的容器解决的时候,该怎么办呢?

这个时候就需要自定义容器了,那么具体我们该怎么做呢?

Expand Down Expand Up @@ -78,3 +82,5 @@ class FunctionalList:
return self.values[:n]

```


8 changes: 6 additions & 2 deletions Article/python10/6.md → Article/PythonBasis/python10/6.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ num1 >= num2 ? --------> False
|`__xor__(self, other)`|实现了位操作 `^`|


最后,如果对本文感兴趣的,可以关注下公众号:
可以关注下公众号:

这个公号可能很少更新,但是一更新,就是把整理的一系列文章更新上去。

![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-WechatIMG697.jpeg)


![公众号](http://twowater.com.cn/images/20171204192251900.gif)
11 changes: 11 additions & 0 deletions Article/PythonBasis/python10/Preface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 前言 #

有时候修改文章,真的修改到想死。真的很耗时间,很烦的。

好吧,每次都是安慰自己,快完结了,快更新完了。

# 目录 #

![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python%20%E7%9A%84%20Magic%20Method.png)


16 changes: 13 additions & 3 deletions Article/python11/1.md → Article/PythonBasis/python11/1.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ DEC = 12

那有没有什么好的方法呢?

这时候我们定义一个 class 类型,每个常量都是 class 里面唯一的实例。正好 Python 提供了 Enum 类来实现这个功能如下:
这时候我们定义一个 class 类型,每个常量都是 class 里面唯一的实例。

正好 Python 提供了 Enum 类来实现这个功能如下:

```python
#!/usr/bin/env python3
Expand All @@ -37,6 +39,14 @@ print('\n', Month.Jan)
输出的结果如下:


![Python3 枚举类型的使用](http://p1ceh5usj.bkt.clouddn.com/Python3%20%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B%E7%9A%84%E4%BD%BF%E7%94%A8.png)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python3%20%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B%E7%9A%84%E4%BD%BF%E7%94%A8.png)

我们使用 `Enum` 来定义了一个枚举类。

上面的代码,我们创建了一个有关月份的枚举类型 Month ,这里要注意的是构造参数,第一个参数 Month 表示的是该枚举类的类名,第二个 tuple 参数,表示的是枚举类的值;当然,枚举类通过 `__members__` 遍历它的所有成员的方法。

注意的一点是 , `member.value` 是自动赋给成员的 `int` 类型的常量,默认是从 1 开始的。

**而且 Enum 的成员均为单例(Singleton),并且不可实例化,不可更改**


可见,我们可以直接使用 `Enum` 来定义一个枚举类。上面的代码,我们创建了一个有关月份的枚举类型 Month ,这里要注意的是构造参数,第一个参数 Month 表示的是该枚举类的类名,第二个 tuple 参数,表示的是枚举类的值;当然,枚举类通过 `__members__` 遍历它的所有成员的方法。注意的一点是 , `member.value` 是自动赋给成员的 `int`类型的常量,默认是从 1 开始的。而且 Enum 的成员均为单例(Singleton),并且不可实例化,不可更改
14 changes: 11 additions & 3 deletions Article/python11/2.md → Article/PythonBasis/python11/2.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# 二、Enum 的源码 #

通过上面的实例可以知道通过 `__members__` 可以遍历枚举类的所有成员。那为什么呢?
通过上面的实例可以知道通过 `__members__` 可以遍历枚举类的所有成员。

我们可以先来大致看看 Enum 的源码是如何实现的;Enum 在模块 enum.py 中,先来看看 Enum 类的片段
那有没有想过为什么呢?

当你看到那段代码的时候,有没有想过为什么通过 `__members__` 就能遍历枚举类型的所有成员出来?


我们可以先来大致看看 Enum 的源码是如何实现的;

Enum 在模块 enum.py 中,先来看看 Enum 类的片段

```python
class Enum(metaclass=EnumMeta):
Expand All @@ -25,4 +32,5 @@ class EnumMeta(type):
return MappingProxyType(cls._member_map_)
```

首先 `__members__` 方法返回的是一个包含一个 Dict 既 Map 的 MappingProxyType,并且通过 @property 将方法 `__members__(cls)` 的访问方式改变为了变量的的形式,既可以直接通过 `__members__` 来进行访问了
首先 `__members__` 方法返回的是一个包含一个 Dict 既 Map 的 MappingProxyType,并且通过 @property 将方法 `__members__(cls)` 的访问方式改变为了变量的的形式,那么就可以直接通过 `__members__` 来进行访问了

4 changes: 3 additions & 1 deletion Article/python11/3.md → Article/PythonBasis/python11/3.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ if __name__ == '__main__':

输出的结果如下:

![Python3 自定义类型的枚举类](http://p1ceh5usj.bkt.clouddn.com/Python3%20%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%9E%9A%E4%B8%BE%E7%B1%BB.png)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python3%20%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%9E%9A%E4%B8%BE%E7%B1%BB.png)



通过上面的例子,可以知道枚举模块定义了具有迭代 (interator) 和比较(comparison) 功能的枚举类型。 它可以用来为值创建明确定义的符号,而不是使用具体的整数或字符串。


File renamed without changes.
11 changes: 11 additions & 0 deletions Article/PythonBasis/python11/Preface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 前言 #

2019年10月14日16:59:38 看了一下,还有五个章节就修改完基础部分了。

干就完事了。

# 目录 #

![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E6%9E%9A%E4%B8%BE%E7%B1%BB.png)


8 changes: 7 additions & 1 deletion Article/python12/1.md → Article/PythonBasis/python12/1.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

在了解元类之前,我们先进一步理解 Python 中的类,在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在 Python 中这一点也是一样的。

这点在学习类的章节也强调过了,下面可以通过例子回忆一下:

```python
class ObjectCreator(object):
pass
Expand Down Expand Up @@ -30,7 +32,9 @@ class ObjectCreator(object):
pass
```

当程序运行这段代码的时候,就会在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它的本质仍然是一个对象,于是我们可以对它做如下的操作:
当程序运行这段代码的时候,就会在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。

但是,它的本质仍然是一个对象,于是我们可以对它做如下的操作:

```python
class ObjectCreator(object):
Expand Down Expand Up @@ -61,3 +65,5 @@ print(objectCreator)
<class '__main__.ObjectCreator'>
<class '__main__.ObjectCreator'>
```


39 changes: 32 additions & 7 deletions Article/python12/2.md → Article/PythonBasis/python12/2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# 二、使用 `type()` 动态创建类 #

因为类也是对象,所以我们可以在程序运行的时候创建类。Python 是动态语言。动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。在之前,我们先了了解下 `type()` 函数。
因为类也是对象,所以我们可以在程序运行的时候创建类。

Python 是动态语言。

**动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。**

在之前,我们先了了解下 `type()` 函数。


首先我们新建一个 `hello.py` 的模块,然后定义一个 Hello 的 class ,
Expand All @@ -11,7 +17,9 @@ class Hello(object):
print('Hello,', name)
```

然后在另一个模块中引用 hello 模块,并输出相应的信息。其中 `type()` 函数的作用是可以查看一个类型和变量的类型。
然后在另一个模块中引用 hello 模块,并输出相应的信息。

其中 `type()` 函数的作用是可以查看一个类型和变量的类型。

```python
#!/usr/bin/env python3
Expand All @@ -35,9 +43,19 @@ Hello, Py
<class 'com.twowater.hello.Hello'>
```

上面也提到过,`type()` 函数可以查看一个类型或变量的类型,`Hello` 是一个 `class` ,它的类型就是 `type` ,而 `h` 是一个实例,它的类型就是 `com.twowater.hello.Hello`。前面的 `com.twowater` 是我的包名,`hello` 模块在该包名下。
上面也提到过,`type()` 函数可以查看一个类型或变量的类型,`Hello` 是一个 `class` ,它的类型就是 `type` ,而 `h` 是一个实例,它的类型就是 `com.twowater.hello.Hello`

前面的 `com.twowater` 是我的包名,`hello` 模块在该包名下。

在这里还要细想一下,上面的例子中,我们使用 `type()` 函数查看一个类型或者变量的类型。

其中查看了一个 `Hello` class 的类型,打印的结果是: `<class 'type'>`

在这里还要细想一下,上面的例子中,我们使用 `type()` 函数查看一个类型或者变量的类型。其中查看了一个 `Hello` class 的类型,打印的结果是: `<class 'type'>` 。其实 `type()` 函数不仅可以返回一个对象的类型,也可以创建出新的类型。class 的定义是运行时动态创建的,而创建 class 的方法就是使用 `type()` 函数。比如我们可以通过 `type()` 函数创建出上面例子中的 `Hello` 类,具体看下面的代码:
**其实 `type()` 函数不仅可以返回一个对象的类型,也可以创建出新的类型。**

class 的定义是运行时动态创建的,而创建 class 的方法就是使用 `type()` 函数。

比如我们可以通过 `type()` 函数创建出上面例子中的 `Hello` 类,具体看下面的代码:

```python
# -*- coding: UTF-8 -*-
Expand Down Expand Up @@ -83,8 +101,15 @@ Hello, Py
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
```

好了,了解完具体的参数使用之外,我们看看输出的结果,可以看到,通过 `type()` 函数创建的类和直接写 class 是完全一样的,因为Python 解释器遇到 class 定义时,仅仅是扫描一下 class 定义的语法,然后调用 `type()` 函数创建出 class 的 。
好了,了解完具体的参数使用之外,我们看看输出的结果,可以看到,通过 `type()` 函数创建的类和直接写 class 是完全一样的。

这是因为Python 解释器遇到 class 定义时,仅仅是扫描一下 class 定义的语法,然后调用 `type()` 函数创建出 class 的。

不过一般的情况下,我们都是使用 `class ***...` 的方法来定义类的,不过 `type()` 函数也可以让我们创建出类来。

也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

**可以看到,在 Python 中,类也是对象,你可以动态的创建类。**

不过一般的情况下,我们都是使用 `class ***...` 的方法来定义类的,不过 `type()` 函数也可以让我们创建出类来。也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。
其实这也就是当你使用关键字 class 时 Python 在幕后做的事情,而这就是通过元类来实现的。

可以看到,在 Python 中,类也是对象,你可以动态的创建类。其实这也就是当你使用关键字 class 时 Python 在幕后做的事情,而这就是通过元类来实现的。
Loading

0 comments on commit d7a75da

Please sign in to comment.