提交 d7a75dac 编写于 作者: T TwoWater

所有基础文章修改完毕

上级 95ad9778
# 一、Python 的 Magic Method #
在 Python 中,所有以 "__" 双下划线包起来的方法,都统称为"魔术方法"。比如我们接触最多的 `__init__` 。魔术方法有什么作用呢?
在 Python 中,所有以 "__" 双下划线包起来的方法,都统称为"魔术方法"。比如我们接触最多的 `__init__`
魔术方法有什么作用呢?
使用这些魔术方法,我们可以构造出优美的代码,将复杂的逻辑封装成简单的方法。
......@@ -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__']
```
可以看到,一个类的魔术方法还是挺多的,不过我们只需要了解一些常见和常用的魔术方法就好了。
可以看到,一个类的魔术方法还是挺多的,截图也没有截全,不过我们只需要了解一些常见和常用的魔术方法就好了。
# 二、构造(`__new__`)和初始化(`__init__`) #
通过上一篇的内容,我们已经知道定义一个类时,我们经常会通过 `__init__(self)` 的方法在实例化对象的时候,对属性进行设置。比如下面的例子:
通过之前的学习,我们已经知道定义一个类时,我们经常会通过 `__init__(self)` 的方法在实例化对象的时候,对属性进行设置。
比如下面的例子:
```python
#!/usr/bin/env python3
......@@ -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` 的任何值
......@@ -57,3 +61,5 @@ if __name__ == '__main__':
其实在实际开发中,很少会用到 `__new__` 方法,除非你希望能够控制类的创建。通常讲到 `__new__` ,都是牵扯到 `metaclass`(元类)的。
当然当一个对象的生命周期结束的时候,析构函数 `__del__` 方法会被调用。但是这个方法是 Python 自己对对象进行垃圾回收的。
# 四、对象的描述器 #
一般来说,一个描述器是一个有“绑定行为”的对象属性 (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 等字段, 就是通过描述器来实现功能的。
我们先看下下面的例子:
......@@ -129,3 +139,5 @@ if __name__ == '__main__':
我们只是修改了 meter ,并且将其赋值成为 int ,但 foot 也修改了。这是 `__set__` 发挥了作用.
描述器对象 (Meter、Foot) 不能独立存在, 它需要被另一个所有者类 (Distance) 所持有。描述器对象可以访问到其拥有者实例的属性,比如例子中 Foot 的 `instance.meter`
# 五、自定义容器(Container) #
经过之前编章的介绍,我们知道在 Python 中,常见的容器类型有: dict, tuple, list, string。其中也提到过可容器和不可变容器的概念。其中 tuple, string 是不可变容器,dict, list 是可变容器。 可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。当然具体的介绍,可以看回之前的文章,有图文介绍。
经过之前编章的介绍,我们知道在 Python 中,常见的容器类型有: dict, tuple, list, string。其中也提到过可容器和不可变容器的概念。其中 tuple, string 是不可变容器,dict, list 是可变容器。
那么这里先提出一个问题,这些数据结构就够我们开发使用吗?不够的时候,或者说有些特殊的需求不能单单只使用这些基本的容器解决的时候,该怎么办呢?
可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。当然具体的介绍,可以看回之前的文章,有图文介绍。
那么这里先提出一个问题,这些数据结构就够我们开发使用吗?
不够的时候,或者说有些特殊的需求不能单单只使用这些基本的容器解决的时候,该怎么办呢?
这个时候就需要自定义容器了,那么具体我们该怎么做呢?
......@@ -78,3 +82,5 @@ class FunctionalList:
return self.values[:n]
```
......@@ -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)
# 前言 #
有时候修改文章,真的修改到想死。真的很耗时间,很烦的。
好吧,每次都是安慰自己,快完结了,快更新完了。
# 目录 #
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python%20%E7%9A%84%20Magic%20Method.png)
......@@ -15,7 +15,9 @@ DEC = 12
那有没有什么好的方法呢?
这时候我们定义一个 class 类型,每个常量都是 class 里面唯一的实例。正好 Python 提供了 Enum 类来实现这个功能如下:
这时候我们定义一个 class 类型,每个常量都是 class 里面唯一的实例。
正好 Python 提供了 Enum 类来实现这个功能如下:
```python
#!/usr/bin/env python3
......@@ -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),并且不可实例化,不可更改
# 二、Enum 的源码 #
通过上面的实例可以知道通过 `__members__` 可以遍历枚举类的所有成员。那为什么呢?
通过上面的实例可以知道通过 `__members__` 可以遍历枚举类的所有成员。
我们可以先来大致看看 Enum 的源码是如何实现的;Enum 在模块 enum.py 中,先来看看 Enum 类的片段
那有没有想过为什么呢?
当你看到那段代码的时候,有没有想过为什么通过 `__members__` 就能遍历枚举类型的所有成员出来?
我们可以先来大致看看 Enum 的源码是如何实现的;
Enum 在模块 enum.py 中,先来看看 Enum 类的片段
```python
class Enum(metaclass=EnumMeta):
......@@ -25,4 +32,5 @@ class EnumMeta(type):
return MappingProxyType(cls._member_map_)
```
首先 `__members__` 方法返回的是一个包含一个 Dict 既 Map 的 MappingProxyType,并且通过 @property 将方法 `__members__(cls)` 的访问方式改变为了变量的的形式,既可以直接通过 `__members__` 来进行访问了
\ No newline at end of file
首先 `__members__` 方法返回的是一个包含一个 Dict 既 Map 的 MappingProxyType,并且通过 @property 将方法 `__members__(cls)` 的访问方式改变为了变量的的形式,那么就可以直接通过 `__members__` 来进行访问了
......@@ -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) 功能的枚举类型。 它可以用来为值创建明确定义的符号,而不是使用具体的整数或字符串。
# 前言 #
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)
......@@ -2,6 +2,8 @@
在了解元类之前,我们先进一步理解 Python 中的类,在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在 Python 中这一点也是一样的。
这点在学习类的章节也强调过了,下面可以通过例子回忆一下:
```python
class ObjectCreator(object):
pass
......@@ -30,7 +32,9 @@ class ObjectCreator(object):
pass
```
当程序运行这段代码的时候,就会在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它的本质仍然是一个对象,于是我们可以对它做如下的操作:
当程序运行这段代码的时候,就会在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。
但是,它的本质仍然是一个对象,于是我们可以对它做如下的操作:
```python
class ObjectCreator(object):
......@@ -61,3 +65,5 @@ print(objectCreator)
<class '__main__.ObjectCreator'>
<class '__main__.ObjectCreator'>
```
# 二、使用 `type()` 动态创建类 #
因为类也是对象,所以我们可以在程序运行的时候创建类。Python 是动态语言。动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。在之前,我们先了了解下 `type()` 函数。
因为类也是对象,所以我们可以在程序运行的时候创建类。
Python 是动态语言。
**动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。**
在之前,我们先了了解下 `type()` 函数。
首先我们新建一个 `hello.py` 的模块,然后定义一个 Hello 的 class ,
......@@ -11,7 +17,9 @@ class Hello(object):
print('Hello,', name)
```
然后在另一个模块中引用 hello 模块,并输出相应的信息。其中 `type()` 函数的作用是可以查看一个类型和变量的类型。
然后在另一个模块中引用 hello 模块,并输出相应的信息。
其中 `type()` 函数的作用是可以查看一个类型和变量的类型。
```python
#!/usr/bin/env python3
......@@ -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 -*-
......@@ -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 在幕后做的事情,而这就是通过元类来实现的。
\ No newline at end of file
# 三、什么是元类 #
通过上面的介绍,终于模模糊糊的带到元类这里来了。可是我们到现在还不知道元类是什么东东
通过上面的介绍,终于模模糊糊的带到元类这里来了。可是我们到现在还不知道元类是什么鬼东西
我们创建类的时候,大多数是为了创建类的实例对象。那么元类呢?元类就是用来创建类的。也可以换个理解方式就是:元类就是类的类。
我们创建类的时候,大多数是为了创建类的实例对象。
那么元类呢?
**元类就是用来创建类的。也可以换个理解方式就是:元类就是类的类。**
通过上面 `type()` 函数的介绍,我们知道可以通过 `type()` 函数创建类:
......@@ -10,11 +14,21 @@
MyClass = type('MyClass', (), {})
```
实际上 `type()` 函数是一个元类。`type()` 就是 Python 在背后用来创建所有类的元类。
**实际上 `type()` 函数是一个元类。**
`type()` 就是 Python 在背后用来创建所有类的元类。
那么现在我们也可以猜到一下为什么 `type()` 函数是 type 而不是 Type呢?
这可能是为了和 str 保持一致性,str 是用来创建字符串对象的类,而 int 是用来创建整数对象的类。type 就是创建类对象的类。你可以通过检查 `__class__` 属性来看到这一点。Python 中所有的东西,注意喔,这里是说所有的东西,他们都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来。
这可能是为了和 str 保持一致性,str 是用来创建字符串对象的类,而 int 是用来创建整数对象的类。
type 就是创建类对象的类。
你可以通过检查 `__class__` 属性来看到这一点。
Python 中所有的东西,注意喔,这里是说所有的东西,他们都是对象。
这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来。
```python
# 整形
......@@ -52,7 +66,9 @@ print(mEat.__class__)
<class '__main__.eat'>
```
可以看到,上面的所有东西,也就是所有对象都是通过类来创建的,那么我们可能会好奇,`__class__``__class__` 会是什么呢?换个说法就是,创建这些类的类是什么呢?
可以看到,上面的所有东西,也就是所有对象都是通过类来创建的,那么我们可能会好奇,`__class__``__class__` 会是什么呢?
**换个说法就是,创建这些类的类是什么呢?**
我们可以继续在上面的代码基础上新增下面的代码:
......@@ -72,6 +88,15 @@ print(mEat.__class__.__class__)
<class 'type'>
```
认真观察,再理清一下,上面输出的结果是我们把整形 `age` ,字符创 `name` ,函数 `fu` 和对象实例 `mEat``__class__``__class__` 打印出来的结果。也可以说是他们类的类打印结果。发现打印出来的 class 都是 type 。
认真观察,再理清一下,上面输出的结果是我们把整形 `age` ,字符创 `name` ,函数 `fu` 和对象实例 `mEat``__class__``__class__` 打印出来的结果。
也可以说是他们类的类打印结果。发现打印出来的 class 都是 type 。
一开始也提到了,元类就是类的类。
也就是元类就是负责创建类的一种东西。
你也可以理解为,元类就是负责生成类的。
**而 type 就是内建的元类。也就是 Python 自带的元类。**
一开始也提到了,元类就是类的类。也就是元类就是负责创建类的一种东西。你也可以理解为,元类就是负责生成类的。而 type 就是内建的元类。也就是 Python 自带的元类。
\ No newline at end of file
# 四、自定义元类 #
到现在,我们已经知道元类是什么东东了。那么,从始至终我们还不知道元类到底有啥用。只是了解了一下元类。在了解它有啥用的时候,我们先来了解下怎么自定义元类。因为只有了解了怎么自定义才能更好的理解它的作用。
到现在,我们已经知道元类是什么鬼东西了。
那么,从始至终我们还不知道元类到底有啥用。
只是了解了一下元类。
在了解它有啥用的时候,我们先来了解下怎么自定义元类。
因为只有了解了怎么自定义才能更好的理解它的作用。
首先我们来了解下 `__metaclass__` 属性
......@@ -8,11 +16,15 @@ metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
但是如果我们想创建出类呢?
那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
所以,metaclass 允许你创建类或者修改类。
换句话说,你可以把类看成是 metaclass 创建出来的“实例”。
```python
class MyObject(object):
......@@ -20,7 +32,11 @@ class MyObject(object):
[]
```
如果是这样写的话,Python 就会用元类来创建类 MyObject。当你写下 `class MyObject(object)`,但是类对象 MyObject 还没有在内存中创建。Python 会在类的定义中寻找 `__metaclass__` 属性,如果找到了,Python 就会用它来创建类 MyObject,如果没有找到,就会用内建的 type 函数来创建这个类。如果还不怎么理解,看下下面的流程图:
如果是这样写的话,Python 就会用元类来创建类 MyObject。
当你写下 `class MyObject(object)`,但是类对象 MyObject 还没有在内存中创建。P
ython 会在类的定义中寻找 `__metaclass__` 属性,如果找到了,Python 就会用它来创建类 MyObject,如果没有找到,就会用内建的 type 函数来创建这个类。如果还不怎么理解,看下下面的流程图:
![__metaclass__的介绍](https://user-gold-cdn.xitu.io/2017/9/6/06c5a4390887abd3d79401848742f5ce)
......@@ -43,7 +59,9 @@ class Foo(Bar):
答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到 type 或者子类化 type 的东东都可以。
元类的主要目的就是为了当创建类时能够自动地改变类。通常,你会为API 做这样的事情,你希望可以创建符合当前上下文的类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定`__metaclass__` 。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。
**元类的主要目的就是为了当创建类时能够自动地改变类。**
通常,你会为API 做这样的事情,你希望可以创建符合当前上下文的类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定`__metaclass__` 。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。
幸运的是,`__metaclass__` 实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。
......@@ -141,4 +159,5 @@ class UpperAttrMetaclass(type):
* 拦截类的创建
* 修改类
* 返回修改之后的类
\ No newline at end of file
* 返回修改之后的类
......@@ -19,7 +19,11 @@ guy = Person(name='bob', age='35')
print guy.age
```
这并不会返回一个 IntegerField 对象,而是会返回一个 int,甚至可以直接从数据库中取出数据。这是有可能的,因为 models.Model 定义了 `__metaclass__` , 并且使用了一些魔法能够将你刚刚定义的简单的Person类转变成对数据库的一个复杂 hook。Django 框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的 API 将其化简,通过这个 API 重新创建代码,在背后完成真正的工作。
这并不会返回一个 IntegerField 对象,而是会返回一个 int,甚至可以直接从数据库中取出数据。
这是有可能的,因为 models.Model 定义了 `__metaclass__` , 并且使用了一些魔法能够将你刚刚定义的简单的Person类转变成对数据库的一个复杂 hook。
Django 框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的 API 将其化简,通过这个 API 重新创建代码,在背后完成真正的工作。
Python 中的一切都是对象,它们要么是类的实例,要么是元类的实例,除了 type。type 实际上是它自己的元类,在纯 Python 环境中这可不是你能够做到的,这是通过在实现层面耍一些小手段做到的。
......@@ -28,6 +32,6 @@ Python 中的一切都是对象,它们要么是类的实例,要么是元类
[https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python](https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python)
最后如果对本文有兴趣,可以关注公众号:
![公众号](http://twowater.com.cn/images/20171204192251900.gif)
# 前言 #
Python 界的领袖 Tim Peters 说的:
> 元类就是深度的魔法,99% 的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。
所以,这篇文章,认真阅读一遍就好了。
# 目录 #
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E5%85%83%E7%B1%BB.png)
......@@ -2,20 +2,34 @@
线程与进程是操作系统里面的术语,简单来讲,每一个应用程序都有一个自己的进程。
操作系统会为这些进程分配一些执行资源,例如内存空间等。在进程中,又可以创建一些线程,他们共享这些内存空间,并由操作系统调用,以便并行计算。
操作系统会为这些进程分配一些执行资源,例如内存空间等。
我们都知道现代操作系统比如 Mac OS X,UNIX,Linux,Windows 等可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听敲代码,一边用 Markdown 写博客,这就是多任务,至少同时有 3 个任务正在运行。当然还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开 PyCharm 就是一个启动了一个 PtCharm 进程,打开 Markdown 就是启动了一个 Md 的进程。
在进程中,又可以创建一些线程,他们共享这些内存空间,并由操作系统调用,以便并行计算。
虽然现在多核 CPU 已经非常普及了。可是由于 CPU 执行代码都是顺序执行的,这时候我们就会有疑问,单核 CPU 是怎么执行多任务的呢?
我们都知道现代操作系统比如 Mac OS X,UNIX,Linux,Windows 等可以同时运行多个任务。
其实就是操作系统轮流让各个任务交替执行,任务 1 执行 0.01 秒,切换到任务 2 ,任务 2 执行 0.01 秒,再切换到任务 3 ,执行 0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于 CPU的执行速度实在是太快了,我们肉眼和感觉上没法识别出来,就像所有任务都在同时执行一样。
打个比方,你一边在用浏览器上网,一边在听敲代码,一边用 Markdown 写博客,这就是多任务,至少同时有 3 个任务正在运行。
当然还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开 PyCharm 就是一个启动了一个 PtCharm 进程,打开 Markdown 就是启动了一个 Md 的进程。
虽然现在多核 CPU 已经非常普及了。
可是由于 CPU 执行代码都是顺序执行的,这时候我们就会有疑问,单核 CPU 是怎么执行多任务的呢?
其实就是操作系统轮流让各个任务交替执行,任务 1 执行 0.01 秒,切换到任务 2 ,任务 2 执行 0.01 秒,再切换到任务 3 ,执行 0.01秒……这样反复执行下去。
表面上看,每个任务都是交替执行的,但是,由于 CPU的执行速度实在是太快了,我们肉眼和感觉上没法识别出来,就像所有任务都在同时执行一样。
真正的并行执行多任务只能在多核 CPU 上实现,但是,由于任务数量远远多于 CPU 的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
有些进程不仅仅只是干一件事的啊,比如浏览器,我们可以播放时视频,播放音频,看文章,编辑文章等等,其实这些都是在浏览器进程中的子任务。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,一个进程也可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。
当然,一个进程也可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。
那么在 Python 中我们要同时执行多个任务怎么办?
......@@ -35,4 +49,9 @@
同时执行多个任务通常各个任务之间并不是没有关联的,而是需要相互通信和协调,有时,任务 1 必须暂停等待任务 2 完成后才能继续执行,有时,任务 3 和任务 4 又不能同时执行,所以,多进程和多线程的程序的复杂度要远远高于我们前面写的单进程单线程的程序。
因为复杂度高,调试困难,所以,不是迫不得已,我们也不想编写多任务。但是,有很多时候,没有多任务还真不行。想想在电脑上看电影,就必须由一个线程播放视频,另一个线程播放音频,否则,单线程实现的话就只能先把视频播放完再播放音频,或者先把音频播放完再播放视频,这显然是不行的。
\ No newline at end of file
因为复杂度高,调试困难,所以,不是迫不得已,我们也不想编写多任务。
但是,有很多时候,没有多任务还真不行。
想想在电脑上看电影,就必须由一个线程播放视频,另一个线程播放音频,否则,单线程实现的话就只能先把视频播放完再播放音频,或者先把音频播放完再播放视频,这显然是不行的。
......@@ -23,6 +23,8 @@ Python 提供两个模块进行多线程的操作,分别是 `thread` 和 `thre
前者是比较低级的模块,用于更底层的操作,一般应用级别的开发不常用。
因此,我们使用 `threading` 来举个例子:
```python
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
......@@ -178,11 +180,17 @@ r_lock = threading.RLock()
## 4、Condition 条件变量 ##
实用锁可以达到线程同步,但是在更复杂的环境,需要针对锁进行一些条件判断。Python 提供了 Condition 对象。使用 Condition 对象可以在某些事件触发或者达到特定的条件后才处理数据,Condition 除了具有 Lock 对象的 acquire 方法和 release 方法外,还提供了 wait 和 notify 方法。线程首先 acquire 一个条件变量锁。如果条件不足,则该线程 wait,如果满足就执行线程,甚至可以 notify 其他线程。其他处于 wait 状态的线程接到通知后会重新判断条件。
实用锁可以达到线程同步,但是在更复杂的环境,需要针对锁进行一些条件判断。
Python 提供了 Condition 对象。
**使用 Condition 对象可以在某些事件触发或者达到特定的条件后才处理数据,Condition 除了具有 Lock 对象的 acquire 方法和 release 方法外,还提供了 wait 和 notify 方法。**
其中条件变量可以看成不同的线程先后 acquire 获得锁,如果不满足条件,可以理解为被扔到一个( Lock 或 RLock )的 waiting 池。直达其他线程 notify 之后再重新判断条件。不断的重复这一过程,从而解决复杂的同步问题
线程首先 acquire 一个条件变量锁。如果条件不足,则该线程 wait,如果满足就执行线程,甚至可以 notify 其他线程。其他处于 wait 状态的线程接到通知后会重新判断条件
![Condition](http://p1ceh5usj.bkt.clouddn.com/Condition.png)
其中条件变量可以看成不同的线程先后 acquire 获得锁,如果不满足条件,可以理解为被扔到一个( Lock 或 RLock )的 waiting 池。直到其他线程 notify 之后再重新判断条件。不断的重复这一过程,从而解决复杂的同步问题。
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Condition.png)
该模式常用于生产者消费者模式,具体看看下面在线购物买家和卖家的示例:
......@@ -382,3 +390,5 @@ for i in t1:
## 6、后台线程 ##
默认情况下,主线程退出之后,即使子线程没有 join。那么主线程结束后,子线程也依然会继续执行。如果希望主线程退出后,其子线程也退出而不再执行,则需要设置子线程为后台线程。Python 提供了 `setDeamon` 方法。
# 进程 #
Python 中的多线程其实并不是真正的多线程,如果想要充分地使用多核 CPU 的资源,在 Python 中大部分情况需要使用多进程。Python 提供了非常好用的多进程包 multiprocessing,只需要定义一个函数,Python 会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing 支持子进程、通信和共享数据、执行不同形式的同步,提供了 Process、Queue、Pipe、Lock 等组件。
Python 中的多线程其实并不是真正的多线程,如果想要充分地使用多核 CPU 的资源,在 Python 中大部分情况需要使用多进程。
Python 提供了非常好用的多进程包 multiprocessing,只需要定义一个函数,Python 会完成其他所有事情。
借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing 支持子进程、通信和共享数据、执行不同形式的同步,提供了 Process、Queue、Pipe、Lock 等组件。
## 1、类 Process ##
......@@ -47,7 +51,7 @@ if __name__ == "__main__":
输出的结果:
![多进程输出结果](http://p1ceh5usj.bkt.clouddn.com/%E5%A4%9A%E8%BF%9B%E7%A8%8B%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C.gif)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E5%A4%9A%E8%BF%9B%E7%A8%8B%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C.gif)
## 2、把进程创建成类 ##
......@@ -83,7 +87,7 @@ if __name__ == '__main__':
输出结果如下:
![创建进程类](http://p1ceh5usj.bkt.clouddn.com/%E5%88%9B%E5%BB%BA%E8%BF%9B%E7%A8%8B%E7%B1%BB.gif)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E5%88%9B%E5%BB%BA%E8%BF%9B%E7%A8%8B%E7%B1%BB.gif)
## 3、daemon 属性 ##
......@@ -309,3 +313,5 @@ if __name__ == '__main__':
写进 Queue 的值为:四点水
从 Queue 读取的值为:四点水
```
# 前言 #
学编程,谁没有为线程折腾过啊。
# 目录 #
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E8%8D%89%E6%A0%B9%E5%AD%A6Python%EF%BC%88%E5%8D%81%E4%B8%89%EF%BC%89%20%E7%BA%BF%E7%A8%8B%E5%92%8C%E8%BF%9B%E7%A8%8B.png)
# 目录 #
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E4%BA%86%E8%A7%A3%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.png)
......@@ -91,7 +91,7 @@ print(time)
这里最直接的表现就是全局变量 `time` 至此至终都没有修改过,这里还是用了 `nonlocal` 关键字,表示在函数或其他作用域中使用外层(非全局)变量。那么上面那段代码具体的运行流程是怎样的。我们可以看下下图:
![Python 闭包解决](http://p1ceh5usj.bkt.clouddn.com/python15/Python%20%E9%97%AD%E5%8C%85%E8%A7%A3%E5%86%B3.png)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python%20%E9%97%AD%E5%8C%85%E8%A7%A3%E5%86%B3.png)
这种内部函数的局部作用域中可以访问外部函数局部作用域中变量的行为,我们称为: 闭包。更加直接的表达方式就是,当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。k
......@@ -146,6 +146,5 @@ print(f.__closure__[0].cell_contents)
闭包的过程其实好比类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。
欢迎打开微信扫一扫,关注微信公众号:
![微信公众号](http://twowater.com.cn/images/20171204192251900.gif)
......@@ -210,6 +210,4 @@ print_args('两点水', sex='男', age=99)
```
至此,[草根学 Python](https://github.com/TwoWater/Python) 入门系列文章结束了。如果感兴趣的话,可以关注微信公众号,回复 “Python” 获取更多的 Python 学习资料。
![微信公众号](http://twowater.com.cn/images/20171204192251900.gif)
# 前言 #
之前的文章都是使用[Sublime Text](http://www.sublimetext.com/)来编写 Python 的,主要是为了更好的熟悉和了解 Python ,可是开发效率不高,也不方便,从这章开始,改为使用 Pycharm 了,在之前的篇节[集成开发环境(IDE): PyCharm](https://www.readwithu.com/python1/IDE.html)中介绍了 PyCharm ,如果如要激活软件可以通过授权服务器来激活,具体看这个网址。[JetBrains激活(http://www.imsxm.com/jetbrains-license-server.html)](http://www.imsxm.com/jetbrains-license-server.html)当然你也可以尝试破解, [Pycharm2017.1.1破解方式](http://blog.csdn.net/zyfortirude/article/details/70800681),不过对于软件的升级不方便。
这篇内容非常的重要,也是我用了很多时间写的。基本上把以前写的东西都重新改了一遍。里面的代码都是我一个一个的敲的,图片也是我一个一个制作的。
......
......@@ -2,20 +2,49 @@
在开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在 Python 中,一个 .py 文件就称之为一个模块(Module)
后面我们学习了函数,知道函数是实现一项或多项功能的一段程序,这样就更方便我们重复使用代码
之前我们学习过函数,知道函数是实现一项或多项功能的一段程序 。其实模块就是函数功能的扩展。为什么这么说呢?那是因为模块其实就是实现一项或多项功能的程序块。
紧接着,我们有学了类,类可以封装方法和变量(属性)。这样就更方便我们维护代码了。
我们之前学过,类的结构是这样的:
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-08-034102.png)
而我们要学的模块是这样的:
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-10-175017.png)
在模块中,我们不但可以直接存放变量,还能存放函数,还能存放类。
不知道你们还有没有印象,我们封装函数用的是 `def` , 封装类用的是 `class`
而我们封装模块,是不需要任何语句的。
**在 Python 中,一个 .py 文件就称之为一个模块(Module)。**
可以看下我之前写的例子,在 pychrome 上 ,这样一个 test.py 文件就是一个模块。
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-070013.png)
其实模块就是函数功能的扩展。为什么这么说呢?
那是因为模块其实就是实现一项或多项功能的程序块。
通过上面的定义,不难发现,函数和模块都是用来实现功能的,只是模块的范围比函数广,在模块中,可以有多个函数。
既然了解了什么是模块了,那么为什么需要模块呢?既然有了函数,那为啥那需要模块?
然有了函数,那为啥那需要模块?
最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括 Python 内置的模块和来自第三方的模块。
最大的好处是大大提高了代码的可维护性。
其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括 Python 内置的模块和来自第三方的模块。
使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。
Python 本身就内置了很多非常有用的模块,只要安装完毕,这些模块就可以立刻使用。我们可以尝试找下这些模块,比如我的 Python 安装目录是默认的安装目录,在 C:\Users\Administrator\AppData\Local\Programs\Python\Python36 ,然后找到 Lib 目录,就可以发现里面全部都是模块,没错,这些 `.py` 文件就是模块了。
![python36bin目录](http://upload-images.jianshu.io/upload_images/2136918-5ecc6493206da1ec?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-python36bin%E7%9B%AE%E5%BD%95.png)
其实模块可以分为标准库模块和自定义模块,而刚刚我们看到的 Lib 目录下的都是标准库模块。
......@@ -73,16 +73,24 @@ from modname import name1[, name2[, ... nameN]]
`import` 导入 sys 模块,然后使用 version 属性
![from···import和 import的区别1](http://upload-images.jianshu.io/upload_images/2136918-499dd531d4ce3d72?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-from%C2%B7%C2%B7%C2%B7import%E5%92%8C%20import%E7%9A%84%E5%8C%BA%E5%88%AB1.png)
`from···import` 直接导入 version 属性
![from···import和 import的区别2](http://upload-images.jianshu.io/upload_images/2136918-eea99fc170ed5a07?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![from···import和 import的区别2](media/from%C2%B7%C2%B7%C2%B7import%E5%92%8C%20import%E7%9A%84%E5%8C%BA%E5%88%AB2-2.png)
## 3、from ··· import * ##
通过上面的学习,我们知道了 `from sys import version` 可以直接导入 version 属性。但是如果我们想使用其他的属性呢?比如使用 sys 模块中的 `executable` ,难道又要写多一句 `from sys import executable` ,两个还好,如果三个,四个呢?难道要一直这样写下去?
通过上面的学习,我们知道了 `from sys import version` 可以直接导入 version 属性。
但是如果我们想使用其他的属性呢?
比如使用 sys 模块中的 `executable` ,难道又要写多一句 `from sys import executable` ,两个还好,如果三个,四个呢?
难道要一直这样写下去?
这时候就需要 `from ··· import *` 语句了,这个语句可以把某个模块中的所有方法属性都导入。比如:
......@@ -105,3 +113,5 @@ C:\Users\Administrator\AppData\Local\Programs\Python\Python36\python.exe
```
注意:这提供了一个简单的方法来导入一个模块中的所有方法属性。然而这种声明不该被过多地使用。
......@@ -14,10 +14,12 @@
首先创建了模块 lname ,然后判断一下是否是主模块,如果是主模块就输出 `main` 不是,就输出 `not main` ,首先直接运行该模块,由于该模块是直接使用,而没有被人调用,所以是主模块,因此输出了 `main` ,具体看下图:
![name属性区分模块1](http://upload-images.jianshu.io/upload_images/2136918-d892d81a71cda9d6?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-name%E5%B1%9E%E6%80%A7%E5%8C%BA%E5%88%86%E6%A8%A1%E5%9D%971.png)
然后又创建一个 user_lname 模块,里面只是简单的导入了 lname 模块,然后执行,输出的结果是 `not main` ,因为 lname 模块被该模块调用了,所以不是主模块,输出结果如图:
![name属性区分模块2](http://upload-images.jianshu.io/upload_images/2136918-9137cb874588dded?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-name%E5%B1%9E%E6%80%A7%E5%8C%BA%E5%88%86%E6%A8%A1%E5%9D%972.png)
# 四、包 #
包,其实在上面的一些例子中,都创建了不同的包名了,具体可以仔细观察。在一开始模块的简介中提到,使用模块可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。但是这里也有个问题,如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python 又引入了按目录来组织模块的方法,称为包(Package)。
包,其实在上面的一些例子中,都创建了不同的包名了,具体可以仔细观察。
在一开始模块的简介中提到,使用模块可以避免函数名和变量名冲突。
相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。
但是这里也有个问题,如果不同的人编写的模块名相同怎么办?
为了避免模块名冲突,Python 又引入了按目录来组织模块的方法,称为包(Package)。
比如最开始的例子,就引入了包,这样子做就算有相同的模块名,也不会造成重复,因为包名不同,其实也就是路径不同。如下图,引入了包名后, lname.py 其实变成了 com.Learn.module.nameattributes.lname
![Python 包](http://upload-images.jianshu.io/upload_images/2136918-7f92a0da0bc609d5?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-Python%20%E5%8C%85.png)
仔细观察的人,基本会发现,每一个包目录下面都会有一个 `__init__.py` 的文件,为什么呢?
因为这个文件是必须的,否则,Python 就把这个目录当成普通目录,而不是一个包 。 `__init__.py` 可以是空文件,也可以有Python代码,因为 `__init__.py` 本身就是一个模块,而它对应的模块名就是它的包名。
# 五、作用域 #
学习过 Java 的同学都知道,Java 的类里面可以给方法和属性定义公共的( public )或者是私有的 ( private ),这样做主要是为了我们希望有些函数和属性能给别人使用或者只能内部使用。 通过学习 Python 中的模块,其实和 Java 中的类相似,那么我们怎么实现在一个模块中,有的函数和变量给别人使用,有的函数和变量仅仅在模块内部使用呢?
学习过 Java 的同学都知道,Java 的类里面可以给方法和属性定义公共的( public )或者是私有的 ( private ),这样做主要是为了我们希望有些函数和属性能给别人使用或者只能内部使用。
在 Python 中,是通过 `_` 前缀来实现的。正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,ni12,PI等;类似`__xxx__`这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的 `__name__` 就是特殊变量,还有 `__author__` 也是特殊变量,用来标明作者。注意,我们自己的变量一般不要用这种变量名;类似 `_xxx``__xxx` 这样的函数或变量就是非公开的(private),不应该被直接引用,比如 `_abc``__abc` 等;
通过学习 Python 中的模块,其实和 Java 中的类相似,那么我们怎么实现在一个模块中,有的函数和变量给别人使用,有的函数和变量仅仅在模块内部使用呢?
注意,这里是说不应该,而不是不能。因为 Python 种并没有一种方法可以完全限制访问 private 函数或变量,但是,从编程习惯上不应该引用 private 函数或变量。
在 Python 中,是通过 `_` 前缀来实现的。正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,ni12,PI等;类似`__xxx__`这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的 `__name__` 就是特殊变量,还有 `__author__` 也是特殊变量,用来标明作者。
注意,我们自己的变量一般不要用这种变量名;类似 `_xxx``__xxx` 这样的函数或变量就是非公开的(private),不应该被直接引用,比如 `_abc``__abc` 等;
**这里是说不应该,而不是不能。因为 Python 种并没有一种方法可以完全限制访问 private 函数或变量,但是,从编程习惯上不应该引用 private 函数或变量。**
比如:
......@@ -48,4 +52,6 @@ DiamondVIP2
------------------------
最后扯淡,欢迎加我微信:`androidwed`,进入微信Python讨论群,一起学习讨论。现在微信群只有50几个人.
最后扯淡,欢迎加我微信:`thinktoday2019`, 进入微信 Python 讨论群。
# 前言 #
之前的文章都是使用[Sublime Text](http://www.sublimetext.com/)来编写 Python 的,主要是为了更好的熟悉和了解 Python ,可是开发效率不高,也不方便,从这章开始,改为使用 Pycharm 了,在之前的篇节[集成开发环境(IDE): PyCharm](https://www.readwithu.com/python1/IDE.html)中介绍了 PyCharm ,如果如要激活软件可以通过授权服务器来激活,具体看这个网址。[JetBrains激活(http://www.imsxm.com/jetbrains-license-server.html)](http://www.imsxm.com/jetbrains-license-server.html)当然你也可以尝试破解, [Pycharm2017.1.1破解方式](http://blog.csdn.net/zyfortirude/article/details/70800681),不过对于软件的升级不方便
学习到这里,可以说 Python 基础学习基本接近尾声了
# 目录 #
![草根学Python(八) 模块与包](http://upload-images.jianshu.io/upload_images/2136918-4434f73dc82c0101?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://twowaterimage.oss-cn-beijing.aliyuncs.com/2019-10-14-%E6%A8%A1%E5%9D%97%E4%B8%8E%E5%8C%85.png)
# 前言 #
距离上一篇已经三个多星期了,最近比较累,下班回到家,很早就休息了,所以更新的进度有点慢。
# 目录 #
![草根学Python(十) Python 的 Magic Method](http://p1ceh5usj.bkt.clouddn.com/%E8%8D%89%E6%A0%B9%E5%AD%A6Python%EF%BC%88%E5%8D%81%EF%BC%89%20Python%20%E7%9A%84%20Magic%20Method.png)
# 前言 #
虽然没多少阅读,可是还是坚持写下去。对 Python 感兴趣的童鞋可以加入 Python 学习讨论微信群喔。可以先加我微信,然后拉进群。本人微信:
![微信](http://img.blog.csdn.net/20170626191709373?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVHdvX1dhdGVy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
# 目录 #
![草根学Python(十一) 枚举类](http://p1ceh5usj.bkt.clouddn.com/%E8%8D%89%E6%A0%B9%E5%AD%A6Python%EF%BC%88%E5%8D%81%E4%B8%80%EF%BC%89%20%E6%9E%9A%E4%B8%BE%E7%B1%BB.png)
# 前言 #
第十二篇了,撸起袖子,就是干。
# 目录 #
![草根学Python(十二)元类](http://p1ceh5usj.bkt.clouddn.com/%E8%8D%89%E6%A0%B9%E5%AD%A6Python%EF%BC%88%E5%8D%81%E4%BA%8C%EF%BC%89%20%E5%85%83%E7%B1%BB.png)
# 前言 #
拖了好久,不过还是得坚持。喜欢本文的话可以加下公众号【于你供读】。
# 目录 #
![草根学Python(十三) 线程和进程](http://p1ceh5usj.bkt.clouddn.com/%E8%8D%89%E6%A0%B9%E5%AD%A6Python%EF%BC%88%E5%8D%81%E4%B8%89%EF%BC%89%20%E7%BA%BF%E7%A8%8B%E5%92%8C%E8%BF%9B%E7%A8%8B.png)
# 目录 #
![草根学Python(十四) 一步一步了解正则表达式](https://raw.githubusercontent.com/TwoWater/Python/master/Article/python14/%E8%8D%89%E6%A0%B9%E5%AD%A6Python%EF%BC%88%E5%8D%81%E5%9B%9B%EF%BC%89%20%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E4%BA%86%E8%A7%A3%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.png)
......@@ -92,37 +92,37 @@ IT 行业相对于一般传统行业,发展更新速度更快,一旦停止
- [主模块和非主模块](/Article/PythonBasis/python9/3.md)
- [](/Article/PythonBasis/python9/4.md)
- [作用域](/Article/PythonBasis/python9/5.md)
* [Python 的 Magic Method](/Article/python10/Preface.md)
- [Python 的 Magic Method](/Article/python10/1.md)
- [构造(`__new__`)和初始化(`__init__`)](/Article/python10/2.md)
- [属性的访问控制](/Article/python10/3.md)
- [对象的描述器](/Article/python10/4.md)
- [自定义容器(Container)](/Article/python10/5.md)
- [运算符相关的魔术方法](/Article/python10/6.md)
* [枚举类](/Article/python11/Preface.md)
- [枚举类的使用](/Article/python11/1.md)
- [Enum 的源码](/Article/python11/2.md)
- [自定义类型的枚举](/Article/python11/3.md)
- [枚举的比较](/Article/python11/4.md)
* [元类](/Article/python12/Preface.md)
- [Python 中类也是对象](/Article/python12/1.md)
- [使用 `type()` 动态创建类](/Article/python12/2.md)
- [什么是元类](/Article/python12/3.md)
- [自定义元类](/Article/python12/4.md)
- [使用元类](/Article/python12/5.md)
* [线程与进程](/Article/python13/Preface.md)
- [线程与进程](/Article/python13/1.md)
- [多线程编程](/Article/python13/2.md)
- [进程](/Article/python13/3.md)
* [一步一步了解正则表达式](/Article/python14/Preface.md)
- [初识 Python 正则表达式](/Article/python14/1.md)
- [字符集](/Article/python14/2.md)
- [数量词](/Article/python14/3.md)
- [边界匹配符和组](/Article/python14/4.md)
- [re.sub](/Article/python14/5.md)
- [re.match 和 re.search](/Article/python14/6.md)
* [闭包](/Article/python15/1.md)
* [装饰器](/Article/python16/1.md)
* [Python 的 Magic Method](/Article/PythonBasis/python10/Preface.md)
- [Python 的 Magic Method](/Article/PythonBasis/python10/1.md)
- [构造(`__new__`)和初始化(`__init__`)](/Article/PythonBasis/python10/2.md)
- [属性的访问控制](/Article/PythonBasis/python10/3.md)
- [对象的描述器](/Article/PythonBasis/python10/4.md)
- [自定义容器(Container)](/Article/PythonBasis/python10/5.md)
- [运算符相关的魔术方法](/Article/PythonBasis/python10/6.md)
* [枚举类](/Article/python11/PythonBasis/Preface.md)
- [枚举类的使用](/Article/PythonBasis/python11/1.md)
- [Enum 的源码](/Article/PythonBasis/python11/2.md)
- [自定义类型的枚举](/Article/PythonBasis/python11/3.md)
- [枚举的比较](/Article/PythonBasis/python11/4.md)
* [元类](/Article/PythonBasis/python12/Preface.md)
- [Python 中类也是对象](/Article/PythonBasis/python12/1.md)
- [使用 `type()` 动态创建类](/Article/PythonBasis/python12/2.md)
- [什么是元类](/Article/PythonBasis/python12/3.md)
- [自定义元类](/Article/PythonBasis/python12/4.md)
- [使用元类](/Article/PythonBasis/python12/5.md)
* [线程与进程](/Article/PythonBasis/python13/Preface.md)
- [线程与进程](/Article/PythonBasis/python13/1.md)
- [多线程编程](/Article/PythonBasis/python13/2.md)
- [进程](/Article/PythonBasis/python13/3.md)
* [一步一步了解正则表达式](/Article/PythonBasis/python14/Preface.md)
- [初识 Python 正则表达式](/Article/PythonBasis/python14/1.md)
- [字符集](/Article/PythonBasis/python14/2.md)
- [数量词](/Article/PythonBasis/python14/3.md)
- [边界匹配符和组](/Article/PythonBasis/python14/4.md)
- [re.sub](/Article/PythonBasis/python14/5.md)
- [re.match 和 re.search](/Article/PythonBasis/python14/6.md)
* [闭包](/Article/PythonBasis/python15/1.md)
* [装饰器](/Article/PythonBasis/python16/1.md)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册