Six 提供了一些简单的工具用来封装 Python 2 和 Python 3 之间的差异性,示例代码:
import six
def dispatch_types(value):
if isinstance(value, six.integer_types):
handle_integer(value)
elif isinstance(value, six.class_types):
handle_class(value)
elif isinstance(value, six.string_types):
handle_string(value)
在创建python包的过程中,IDE都会在包根目录下创建一个__init__.py文件,该Python文件默认是空的.目录结构如下:
Pycharm下的package树结构:
Python中的包和模块有两种导入方式:精确导入和模糊导入:
精确导入:
from Root.Pack1 import Pack1Class import Root.Pack1.Pack1Class
模糊导入:
from Root.Pack1 import *
模糊导入中的*中的模块是由__all__来定义的,__init__.py的另外一个作用就是定义package中的__all__,用来模糊导入,如__init__.py:
__all__ = ["Pack1Class","Pack1Class1"]
在包外部调用:
from Root.Pack1 import * a = Pack1Class.Pack1_AA("Alvin") a.PrintName()
__init__.py首先是一个python文件,所以还可以用来写python模块,但是不建议这么写,尽量保证__init__.py足够轻:
__init__.py:
__all__ = ["Pack1Class","Pack1Class1","Init_AA"] class Init_AA: def __init__(self,name): self.name = name def Greeting(self): print("Hello ",self.name)
在测试中调用:
from Root.Pack1 import * b = Init_AA("test") b.Greeting()
总结:
从上边的例子可以看出,__init__.py的主要作用是:
1. Python中package的标识,不能删除
2. 定义__all__用来模糊导入
3. 编写Python代码(不建议在__init__中写python模块,可以在包中在创建另外的模块来写,尽量保证__init__.py简单)
模块要处于Python搜索路径中的目录里才能被导入,但我们不喜欢维护一个永久性的大目录,因为其他所有的Python脚本和应用程序导入模块的时候性能都会被拖累。本节代码动态地在该路径中添加了一个"目录",当然前提是此目录存在而且此前不在sys.path中。
sys.path是个列表,在末尾添加目录用sys.path.append。当这个append执行完之后,新目录即时起效,以后的每次import操作都可能会检查这个目录。sys.path.insert(0,…,这样新添加的目录会优先于其他目录被import检查。
即使sys.path中存在重复,或者一个不存在的目录被不小心添加进来,也没什么大不了,Python的import语句非常聪明,它会自己应付这类问题。但是,如果每次import时都发生这种错误(比如,重复的不成功搜索,操作系统提示的需要进一步处理的错误),我们会被迫付出一点小小的性能代价。为了避免这种无谓的开销,本节代码在向sys.path添加内容时非常谨慎,绝不加入不存在的目录或者重复的目录。程序向sys.path添加的目录只会在此程序的生命周期之内有效,其他所有的对sys.path的动态操作也是如此。
def AddSysPath(new_path):
""" AddSysPath(new_path):给Python的sys.path增加一个"目录"
如果此目录不存在或者已经在sys.path中了,则不操作
返回1表示成功,-1表示new_path不存在,0表示已经在sys.path中了
already on sys.path.
"""
import sys, os
# 避免加入一个不存在的目录
if not os.path.exists(new_path): return -1
# 将路径标准化。 Windows是大小写不敏感的,所以若确定在
# Windows下,将其转成小写
new_path = os.path.abspath(new_path)
if sys.platform == 'win32':
new_pathnew_path = new_path.lower( )
# 检查当前所有的路径
for x in sys.path:
x = os.path.abspath(x)
if sys.platform == 'win32':
xx = x.lower( )
if new_path in (x, x + os.sep):
return 0
sys.path.append(new_path)
# 如果想让new_path在sys.path处于最前
# 使用:sys.path.insert(0, new_path)
return 1
if _ _name_ _ == '_ _main_ _':
# 测试,显示用法
import sys
print 'Before:'
for x in sys.path: print x
if sys.platform == 'win32':
print AddSysPath('c:\\Temp')
print AddSysPath('c:\\temp')
else:
print AddSysPath('/usr/lib/my_modules')
print 'After:'
for x in sys.path: print x
在开头加上from __future__ import print_function这句之后,即使在python2.X,使用print就得像python3.X那样加括号使用。python2.X中print不需要括号,而在python3.X中则需要。
# python2.7
print "Hello world"
# python3
print("Hello world")
如果某个版本中出现了某个新的功能特性,而且这个特性和当前版本中使用的不兼容,也就是它在该版本中不是语言标准,那么我如果想要使用的话就需要从future模块导入。
其他例子:
from __future__ import division ,
from __future__ import absolute_import ,
from __future__ import with_statement 。等等
加上这些,如果你的python版本是python2.X,你也得按照python3.X那样使用这些函数。
range() 函数可创建一个整数列表,一般用在 for 循环中。
range(start, stop[, step])
- start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5);
- stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
- step:步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1)
xrange与range之间的区别
xrange的用法与range相同,即xrange([start,] stop[, step])根据start与stop指定的范围以及step设定的步长,不同的是xrange并不是生成序列,而是作为一个生成器。即他的数据生成一个取出一个。
所以相对来说,xrange比range性能优化很多,因为他不需要一下子开辟一块很大的内存,特别是数据量比较大的时候。
range、arange,两者的区别仅仅是arange返回的是一个数据,而range返回的是list。
-
先引入numpy。
-
range返回从0到4的5个数构成的list,而arange返回一个array对象。不过他们的元素都是一样的。
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
linspace()通过指定开始值、终值和元素个数创建表示等差数列的一维数组,可以通过endpoint参数指定是否包含终值,默认值为True,即包含终值
numpy.random.normal(loc=0.0, scale=1.0, size=None)
loc:float
概率分布的均值,对应着整个分布的中心center
scale:float
概率分布的标准差,对应于分布的宽度,scale越大越矮胖,scale越小,越瘦高
size:int or tuple of ints
输出的shape,默认为None,只输出一个值
我们更经常会用到np.random.randn(size)所谓标准正太分布(μ=0, σ=1),对应于np.random.normal(loc=0, scale=1, size)
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
zip 方法在 Python 2 和 Python 3 中的不同:在 Python 3.x 中为了减少内存,zip() 返回的是一个对象。如需展示列表,需手动 list() 转换。
a = [1,2,3]
b = [4,5,6]
c = [4,5,6,7,8]
zipped = zip(a,b)# 打包为元组的列表
print(list(zipped))
print(list(zip(a,c))) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
[(1, 4), (2, 5), (3, 6)]
Python元组的升级版本 -- namedtuple(具名元组)
因为元组的局限性:不能为元组内部的数据进行命名,所以往往我们并不知道一个元组所要表达的意义,所以在这里引入了 collections.namedtuple 这个工厂函数,来构造一个带字段名的元组。具名元组的实例和普通元组消耗的内存一样多,因为字段名都被存在对应的类里面。这个类跟普通的对象实例比起来也要小一些,因为 Python 不会用 __dict__ 来存放这些实例的属性。
namedtuple 对象的定义如以下格式:
collections.namedtuple(typename, field_names, verbose=False, rename=False)
返回一个具名元组子类 typename,其中参数的意义如下:
- typename:元组名称
- field_names: 元组中元素的名称
- rename: 如果元素名称中含有 python 的关键字,则必须设置为 rename=True
- verbose: 默认就好
下面来看看声明一个具名元组及其实例化的方法:
import collections
# 两种方法来给 namedtuple 定义方法名
#User = collections.namedtuple('User', ['name', 'age', 'id'])
User = collections.namedtuple('User', 'name age id')
user = User('tester', '22', '464643123')
print(user)
输出结果:User(name='tester', age='22', id='464643123')
collections.namedtuple('User', 'name age id') 创建一个具名元组,需要两个参数,一个是类名,另一个是类的各个字段名。后者可以是有多个字符串组成的可迭代对象,或者是有空格分隔开的字段名组成的字符串(比如本示例)。具名元组可以通过字段名或者位置来获取一个字段的信息。
具名元组的特有属性:
类属性 _fields:包含这个类所有字段名的元组 类方法 _make(iterable):接受一个可迭代对象来生产这个类的实例 实例方法 _asdict():把具名元组以 collections.OrdereDict 的形式返回,可以利用它来把元组里的信息友好的展示出来
from collections import namedtuple
# 定义一个namedtuple类型User,并包含name,sex和age属性。
User = namedtuple('User', ['name', 'sex', 'age'])
# 创建一个User对象
user = User(name='Runoob', sex='male', age=12)
# 获取所有字段名
print( user._fields ) #('name', 'sex', 'age')
# 也可以通过一个list来创建一个User对象,这里注意需要使用"_make"方法
user = User._make(['Runoob', 'male', 12])
print( user ) #User(name='Runoob', sex='male', age=12)
# 获取用户的属性
print( user.name ) #Runoob
print( user.sex ) #male
print( user.age ) #12
# 修改对象属性,注意要使用"_replace"方法
user = user._replace(age=22)
print( user ) #User(name='Runoob', sex='male', age=22)
# 将User对象转换成字典,注意要使用"_asdict"
print( user._asdict() )
# OrderedDict([('name', 'Runoob'), ('sex', 'male'), ('age', 22)])
Namedtuple比普通tuple具有更好的可读性,可以使代码更易于维护。同时与字典相比,又更加的轻量和高效。但是有一点需要注意,就是namedtuple中的属性都是不可变的。任何尝试改变其属性值的操作都是非法的。
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。Python 2.3. 以上版本可用,2.6 添加 start 参数。
enumerate(sequence, [start=0])
- sequence -- 一个序列、迭代器或其他支持迭代对象。
- start -- 下标起始位置。
返回值
返回 enumerate(枚举) 对象。
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
list(enumerate(seasons, start=1)) # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
seq = ['one', 'two', 'three']
for i, element in enumerate(seq):
print i, element
故:
(1) dict的key 或者 set的值都必须是可hash的
不可变对象,都是可hash的,str,fronzenset, tuple, 自己实现的类(带有__hash__魔法函数)
(2) dict的内存花销大(hash简单的来说即映射,如图1所示,映射之后,不可能是连续的存在内存空间中的,总有一些内存时空的,当发现内存空间中的“空”只有1/3时,便会触发扩容操作,以免引起hash冲突),但是查询速度快。自定义的对象,或者python内部的对象都是dict包装的。
(3)dict的存储顺序和元素的添加顺序有关
(4)添加的数据有可能改变已有的数据顺序(扩容时,需要将原来的dict,复制移动到新的内存空间,此时将“挤出”已有的“空”,所以每个key的偏移可能改变)
函数的partial应用
函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。
In [9]: from functools import partial
In [10]: def add(a,b):
....: return a+b
In [12]: plus = partial(add,100)
In [13]: plus(9)
Out[13]: 109
b = a[i:j:s]表示:i,j与上面的一样,但s表示步进,缺省为1.
所以a[i:j:1]相当于a[i:j]
当s<0时,i缺省时,默认为-1. j缺省时,默认为-len(a)-1
所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍,即倒序。
在python中对象、类型和元类构成了一个微妙的世界。
1、python中无处不对象
2、所有对象都有三种特性:id、类型、值
下面来逐条进行分析:
1、python中无处不对象
python中的int、tuple、list、dict、set、函数、类以及派生类都是对象,当然类实例化的对象就更应该是对象了吧。
2、所有对象都有三种特性:id、类型、值
id 就是对象在内存中存放的地址。id其实就相当于在内存中的身份标识。用内置函数id()可以查询对象的id。
类型可以用type()来查看
>>> type('a')
<type 'str'>
值就是对象中存放的数据。有的对象不会改变,叫不可变对象,例如:数值、字符串、元组。对象的值可以改变的对象叫可变对象,例如:列表,集合,字典。
实例被创建后,其身份和类型就不可改变。
在py2.X中,新式类需要继承object类,而在py3.x中已经做了更改,在创建所有的类的时候都会默认继承object类。
这里又出现了两个问题:
1,object类是什么?
2,继承是什么?
我们先说第二个问题:
类可以继承和派生,被继承的类叫超类,父类或者基类
再来说说第一个问题:
object类是所有的类的祖宗。这个object其实也是一个对象(python中处处皆对象)。但是这个对象又有一些特别,其他的对象都是一个抽象的对象,而这个object却是一个具体存在的。
什么是元类呢?
list,str,int 这些类型是由谁创建的呢。对了,就是元类。元类创建的类不是一般定义的类,它创建的是模版。最根溯源,我们会找到一个特殊的元类,就是type。type是由它自身创建的。
>>> type(list)
<type 'type'>
>>> type(type)
<type 'type'>
难道它既是父亲,又是儿子?why?好吧,我们换种查询方式。
1 2 |
|
其实type是以object为模版创建的。
是不是解释通了呢?别着急,如果你足够细心,你还会发现一个小问题。
>>> type(object)
<class 'type'>
what? object是由type创建的?
其实创建它们的是一个叫虚拟机的家伙。它是来自另一个世界的。。
某个对象包含对其他对象的引用,则将其称为容器,如:列表。
使用点()运算符可以访问属性和方法。
查询某一种内置类型所支持的内置方法
help(类型)
对象的类型
- 数字:int,long,float,complex,bool
- 字符:str,unicod
- 列表:list
- 字典:dict
- 元组:tuple
- 文件:file
- 其他类型:集合(set),冻结集合(frozenset),类类型等。
类型转换的函数: str(),repr()或format():将非字符型数据转换成字符型
int():转为整型
float():转为浮点型
list():可以将字串转成列表
In [20]: str3 = 'xiangge'
In [21]: l1 = list(str3)
In [23]: print(l1)
['x', 'i', 'a', 'n', 'g', 'g', 'e']
In [24]: type(l1)
Out[24]: builtins.list
tuple():将字串转换成元组
set():将字串转换成集合,集合没有次序,并且把重复的都去掉。
In [20]: str3 = 'xiangge'
In [25]: s1 = set(str3)
In [26]: type(s1)
Out[26]: set
In [27]: print(s1)
{'a', 'e', 'n', 'g', 'i', 'x'}
dict():转换为字典
In [28]: l3 = [('a',1),('b',2),('c',3)]
In [30]: d1 = dict(l3)
In [31]: print(d1)
{'a': 1, 'b': 2, 'c': 3}
chr():将整数转为字符
ord():将字符转换成整数,与chr相反
hex():将整数转换16进制
bin():将整数转2进制
oct(x):将整数转8进制
序列表示索引为非负整数的有序对象集合,包括字符串、列表和元组。字符串和元组属于不可变序列,所有序列都支持迭代。
适用所有序列的操作和方法
- s[i]: 索引运算
- s[i:j]: 为切片运算符
- s[i:j:stride]: 为扩展切片运算符
- len(s): 序列长度
- min(s) : s中最小值
- max(s):s中最大值
- sum(s): s中各项和
- all(s):检查s中的所有项是否为True
- any(s) : 检查s中的任意项是否为True
适用于可变序列的操作
- s[i] = v 项目赋值
- s[i:j] =t 切片赋值
- s[i:j:stride] = t 扩展切片赋值
- del s[i] 项目删除
- del s[i:j] 切片删除
- del s[i:j:stride] 扩展切片删除
2.unicode字符串与r连用必需在r前面,如name=ur'l\thf'
移除空白 S.strip([chars]) -> str 前后空白移除
分割 def split(self, sep=None, maxsplit=-1):
判断字符串中是否全为数字 S.isdigit() -> bool
判断是否全为空格S.isspace() -> bool
判断字符串是否以什么开头 S.startswith(prefix[, start[, end]]) -> bool
判断字符串是否以什么结尾 S.endswith(suffix[, start[, end]]) -> bool
取出字符索引 S.index(sub[, start[, end]]) -> int
列表介绍
定义:[]内以逗号分隔
追加 L.append(object)
L.insert(index, object)
删除 L.pop([index]) #不加参数默认删最后一个,pop(0)删第一个
循环
包含 in 'value' in list
其他常用: L.index(value, [start, [stop]])
extend 和 append区别 L.extend(iterable)
remove 不存在报错,默认删掉第一个找到的。 L.remove(value)
sort(reverse=Trun) L.sort(key=None, reverse=False)
创建元组
ages = (11, 22, 33, 44, 55) 或 ages = tuple((11, 22, 33, 44, 55))
def count(self, value):
T.count(value) -> integer -- return number of occurrences of value
def index(self, value, start=None, stop=None):
T.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
字典常用操作
def clear(self):
def keys(self): # 所有的key列表 """
""" D.keys() -> list of D's keys """
def pop(self, k, d=None): #
""" 获取并在字典中移除 """
"""
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised
def update(self, E=None, **F):
""" 更新
D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
If E present and has a .keys() method, does: for k in E: D[k] = E[k]
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
def values(self): #
""" 所有的值 """
集合介绍
定义:由不同元素组成的集合,集合中是一组无序排列的可hash值,可以作为字典的key
特性: 集合的目的是将不同的值存放到一起,不同的集合间用来做关系运算,无需纠结于集合中单个值
集合创建
{1,2,3,1} 或 定义可变集合set
set_test=set('hello') set_test {'l', 'o', 'e', 'h'}
改为不可变集合frozenset
f_set_test=frozenset(set_test) f_set_test frozenset({'l', 'e', 'h', 'o'})
集合常见操作:
def discard(self, *args, **kwargs):
""" 移除元素 """
If the element is not a member, do nothing.
def intersection(self, *args, **kwargs):
""" 取交集,新创建一个set """
def intersection_update(self, *args, **kwargs):
""" 取交集,修改原来set """
def isdisjoint(self, *args, **kwargs):
""" 如果没有交集,返回true """
def issubset(self, *args, **kwargs):
""" 是否是子集 """
def issuperset(self, *args, **kwargs):
""" 是否是父集 """
def pop(self, *args, **kwargs):
""" 移除 """
Remove and return an arbitrary set element.
Raises KeyError if the set is empty.
def remove(self, *args, **kwargs):
""" 移除 """
"""
Remove an element from a set; it must be a member.
If the element is not a member, raise a KeyError.
def symmetric_difference(self, *args, **kwargs):
""" 差集,创建新对象"""
"""
Return the symmetric difference of two sets as a new set.
def symmetric_difference_update(self, *args, **kwargs):
""" 差集,改变原来 """
def union(self, *args, **kwargs):
""" 并集 """
def update(self, *args, **kwargs):
""" 更新 """
""" Update a set with the union of itself and others. """
文件类型介绍
一、打开文件
1 文件句柄 = file('文件路径', '模式') 注:python中打开文件有两种方式,即:open(...) 和 file(...) ,本质上前者在内部会调用后者来进行文件操作,推荐使用 open。
r,只读模式(默认)。
w,只写模式。【不可读;不存在则创建;存在则删除内容;】
a,追加模式。【可读; 不存在则创建;存在则只追加内容;】
"+" 表示可以同时读写某个文件
r+,可读写文件。【可读;可写;可追加】
w+,写读
a+,同a
"b"表示处理二进制文件(如:FTP发送上传ISO镜像文件,linux可忽略,windows处理二进制文件时需标注)
rb
wb
ab
二、操作操作
def close(self):
关闭文件
"""
close() may be called more than once without error.
def fileno(self): #
文件描述符
"""
fileno() -> integer "file descriptor".
def flush(self): #
刷新文件内部缓冲区
def isatty(self): #
判断文件是否是同意tty设备
def next(self):
获取下一行数据,不存在,则报错
def read(self, size=None):
读取指定字节数据
def readline(self, size=None):
仅读取一行数据
def readlines(self, size=None):
读取所有数据,并根据换行保存值列表
def seek(self, offset, whence=None):
指定文件中指针位置
def tell(self):
获取当前指针位置
def truncate(self, size=None):
截断数据,仅保留指定之前数据
def write(self, p_str):
写内容
def writelines(self, sequence_of_strings):
将一个字符串列表写入文件
def xreadlines(self):
可用于逐行读取文件,非全部
三、with
为了避免打开文件后忘记关闭,可以通过管理上下文,即:
with open('log','r') as f:
3 種不同的方式來達成字串格式化 (String format)。分別是 %-formatting、str.format 以及 f-string。
>>>'Hello, %s'% ('Denny')
'Hello Denny'
>>>'Hello {name:*^15}'.format(name='foobar')
'Hello ****foobar*****'
>>>stock= 'tsmc'
>>>close= 217.5
>>>f'{stock} price: {close}' #推荐的方式
'tsmc price: 217.5'
>>>f'{upper(stock)} price: {close}'
'TSMC price: 217.5'
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list
# def __str__(self):
# return self.__repr__()
def __repr__(self):
return ','.join(self.employee)
company = Company(["11", "22", "33"])
print(company)
类的 __str__ 方法会在某些需要将对象转为字符串的时候被调用。比如下面这些情况
Python 2 中还有一个 __unicode__ 方法
通过下面的操作来感觉下什么时候调用 __str__ ,什么时候调用的 __repr__ 。
从上面可以看出,当我们查看对象的时候(上图的最后一个操作)调用的是 __repr__ 方法。
另外,列表以及字典等容器总是会使用 __repr__ 方法。即使你显式的调用 str 方法,也是如此。
那么 __str__ 和 __repr__ 的差别是什么
我们试着去 Python 的标准库中找找答案。
因此,我们有个初步的答案。
__str__ 的返回结果可读性强。也就是说,__str__ 的意义是得到便于人们阅读的信息,就像上面的 '2018-04-03' 一样。
__repr__ 的返回结果应更准确。怎么说,__repr__ 存在的目的在于调试,便于开发者使用。细心的读者会发现将 __repr__ 返回的方式直接复制到命令行上,是可以直接执行的。
每个类都最好有一个 __repr__ 方法
如果你没有添加 __str__ 方法,Python 在需要该方法但找不到的时候,它会去调用 __repr__ 方法。因此,我推荐在写自己的类的时候至少添加一个 __repr__ 方法。
我们为 Car 类添加一个 __repr__ 方法
注意,我们这里用了 !r 标记,是为了保证 self.color 与 self.mileage 在转化为字符串的时候使用 repr(self.color) 和 repr(self.mileage) ,而不是 str(self.color) 和 str(self.mileage) 。
有个缺点,就是我们把类的名称写死了。
这种写法也有一个不好的地方,就是格式化字符串太长了。
Python 2 中的 __unicode__ 方法
Python 3 中字符串用 str 类型表示,代表 unicode 字符串。而 Python 2 中字符串有两种类型,一是 str ,只能存储 ASCII 码,另一种是 unicode ,与 Python 3 中的 str 等同。
通常来说,用 __unicode__ 来控制类到字符串的转化更容易被大家接受
unicode() 会先找 __unicode__ 方法,找不到的话会调用 __str__ 方法,并将其结果按当时的编码方式解码返回。
相对于Python 3 ,Python 2 中的类到字符串的转化,显得稍微复杂一些。不过,下面我给了个便于实践的思路。由于使用 unicode 处理字符串更方便,这也是趋势,所以我们总会实现自己的 __unicode__ 方法。同时,__str__ 方法的实现则依靠于 __unicode__ ,主要逻辑是调用 __unicode__ 方法并将其结果使用 UTF-8 编码后返回。
所以,大部分情况下,__str__ 方法都不需要做修改,对于新建的类,可以直接把这个 __str__ 方法复制进去,而把关注点只放在 __unicode__ 方法的实现上。
下面是在 Python 2 中一段比较完整的示例
小结
* 在 Python 2 中,我们可能更在意类的 __unicode__ 方法的实现。
python类的一些特殊东东
self 代表的是类的实例,代表当前对象的地址,而 self.__class__ 则指向类
class Test:
def prt(self):
print(self)#<__main__.Test instance at 0x0000000003B81188>
print(self.__class__)#__main__.Test
t = Test()
t.prt()
Python内置类属性
__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
class Employee:
'shuo ming'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
print "Employee.__doc__:", Employee.__doc__ #shuo ming
print "Employee.__name__:", Employee.__name__ #Employee
print "Employee.__module__:", Employee.__module__ #__main__
print "Employee.__bases__:", Employee.__bases__ #()
print "Employee.__dict__:", Employee.__dict__
#{'__module__': '__main__', 'displayCount': <function displayCount at 0x000000000ADF5D68>,
# 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x000000000ADF5DD8>,
# '__doc__': 'shuo ming', '__init__': <function __init__ at 0x000000000ADF5CF8>}
python对象销毁(垃圾回收)
Python 使用了引用计数这一简单技术来跟踪和回收垃圾。
一个内部跟踪变量,称为一个引用计数器。
这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
a = 40 # 创建对象 <40>
b = a # 增加引用, <40> 的计数
c = [b] # 增加引用. <40> 的计数
del a # 减少引用 <40> 的计数
b = 100 # 减少引用 <40> 的计数
c[0] = -1 # 减少引用 <40> 的计数
垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。这种情况下,仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环。
实例
析构函数 __del__
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "delete"
pt1 = Point()
pt2 = pt1
pt3 = pt1
#id方法的返回值就是对象的内存地址
print id(pt1), id(pt2), id(pt3) #60380104 60380104 60380104
del pt1
del pt2
del pt3 #Point delete
issubclass()或者isinstance()方法。
- issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
- isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
基础重载方法
下表列出了一些通用的功能,你可以在自己的类重写:
序号 | 方法, 描述 & 简单的调用 |
---|---|
1 | __init__ ( self [,args...] ) 构造函数 简单的调用方法: obj = className(args) |
2 | __del__( self ) 析构方法, 删除一个对象 简单的调用方法 : del obj |
3 | __repr__( self ) 转化为供解释器读取的形式 简单的调用方法 : repr(obj) |
4 | __str__( self ) 用于将值转化为适于人阅读的形式 简单的调用方法 : str(obj) |
5 | __cmp__ ( self, x ) 对象比较 简单的调用方法 : cmp(obj, x) |
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print v1 + v2 #Vector (7, 8)
单下划线、双下划线、头尾双下划线说明:
__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
Python中特殊方法的分类与总结
>>> Class1().__class__ # 类型 <class '__main__.Class1'>
>>> Class1().__module__ # 实例类型所在模块 '__main__'
>>> Class1().__dict__ # 对象字典,存储所有实例成员信息。 {'i': 1234}
接下来是保留方法,可以把保留方法分类:
类的基础方法
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 初始化一个实例 | x = MyClass() | x.__init__() |
② | 字符串的“官方”表现形式 | repr(x) | x.__repr__() |
③ | 字符串的“非正式”值 | str(x) | x.__str__() |
④ | 字节数组的“非正式”值 | bytes(x) | x.__bytes__() |
⑤ | 格式化字符串的值 | format(x, format_spec) | x.__format__(format_spec) |
- 对
__init__()
方法的调用发生在实例被创建 之后 。如果要控制实际创建进程,请使用__new__()
方法。 - 按照约定,
__repr__()
方法所返回的字符串为合法的 Python 表达式。 - 在调用
print(x)
的同时也调用了__str__()
方法。 - 由于
bytes
类型的引入而从 Python 3 开始出现。
行为方式与迭代器类似的类
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 遍历某个序列 | iter(seq) | seq.__iter__() |
② | 从迭代器中获取下一个值 | next(seq) | seq.__next__() |
③ | 按逆序创建一个迭代器 | reversed(seq) | seq.__reversed __() |
- 无论何时创建迭代器都将调用
__iter__()
方法。这是用初始值对迭代器进行初始化的绝佳之处。 - 无论何时从迭代器中获取下一个值都将调用
__next__()
方法。 __reversed__()
方法并不常用。它以一个现有序列为参数,并将该序列中所有元素从尾到头以逆序排列生成一个新的迭代器。
计算属性
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 获取一个计算属性(无条件的) | x.my_property | x.__getattribute__('my_property') |
② | 获取一个计算属性(后备) | x.my_property | x.__getattr__('my_property') |
③ | 设置某属性 | x.my_property = value | x.__setattr__('my_property',value) |
④ | 删除某属性 | del x.my_property | x.__delattr__('my_property') |
⑤ | 列出所有属性和方法 | dir(x) | x.__dir__() |
- 如果某个类定义了
__getattribute__()
方法,在 每次引用属性或方法名称时Python 都调用它(特殊方法名称除外,因为那样将会导致讨厌的无限循环)。 - 如果某个类定义了
__getattr__()
方法,Python 将只在正常的位置查询属性时才会调用它。如果实例 x 定义了属性color,x.color
将 不会 调用x.__getattr__('color')
;而只会返回x.color已定义好的值。 - 无论何时给属性赋值,都会调用
__setattr__()
方法。 - 无论何时删除一个属性,都将调用
__delattr__()
方法。 - 如果定义了
__getattr__()
或__getattribute__()
方法,__dir__()
方法将非常有用。通常,调用dir(x)
将只显示正常的属性和方法。如果__getattr()__
方法动态处理color 属性,dir(x)
将不会将 color 列为可用属性。可通过覆盖__dir__()
方法允许将color 列为可用属性,对于想使用你的类但却不想深入其内部的人来说,该方法非常有益。
行为方式与函数类似的类
可以让类的实例变得可调用——就像函数可以调用一样——通过定义 __call__()
方法。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 像调用函数一样“调用”一个实例 | my_instance() | my_instance.__call__() |
zipfile
模块 通过该方式定义了一个可以使用给定密码解密 经加密 zip 文件的类。
# excerpt from zipfile.py
class _ZipDecrypter:
def __init__(self, pwd):
self.key0 = 305419896
self.key1 = 591751049
self.key2 = 878082192
for p in pwd:
self._UpdateKeys(p)
def __call__(self, c):
assert isinstance(c, int)
k = self.key2 | 2 c = c ^ (((k * (k^1)) >> & 255)
self._UpdateKeys(c)
return c
zd = _ZipDecrypter(pwd)
bytes = zef_file.read(12)
h = list(map(zd, bytes[0:12]))
_ZipDecryptor
类维护了以三个旋转密钥形式出现的状态,该状态稍后将在_UpdateKeys()
方法中更新(此处未展示)。- 在此例中,
__call__()
对 zip 文件的单个字节进行解密,然后基于经解密的字节对旋转密码进行更新。 - 给出 zip 文件的头 12 个字节,将这些字节映射给 zd 进行解密,实际上这将导致调用
__call__()
方法 12 次
行为方式与序列类似的类
如果类作为一系列值的容器出现。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 序列的长度 | len(seq) | seq.__len__() |
② | 是否包含特定的值 | x in seq | seq.__contains__(x) |
# A script which responds to http://example.com/search?q=cgi
import cgi fs = cgi.FieldStorage()
if 'q' in fs:
do_search()
# An excerpt from cgi.py that explains how that works class FieldStorage: . . .
def __contains__(self, key):
if self.list is None:
raise TypeError('not indexable')
return any(item.name == key for item in self.list)
def __len__(self):
return len(self.keys())
一旦创建了 cgi.FieldStorage
类的实例,就可以使用 “in
” 运算符了。
- 而
__contains__()
方法是令该魔法生效的主角。 if 'q' in fs
,Python 将在 fs 对象中查找__contains__()
方法。'q'
的值被当作key 参数传入__contains__()
方法。-
len(fs)
调用FieldStorage
的__len__()
方。 self.keys()
方法检查self.list is None
是否为真值,因此__len__
方法无需重复该错误检查。
行为方式与字典类似的类
在前一节的基础上稍作拓展,就不仅可以对 “in
” 运算符和 len()
函数进行响应,还可像全功能字典一样根据键来返回值。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 通过键来获取值 | x[key] | x.__getitem__(key) |
② | 通过键来设置值 | x[key] = value | x.__setitem__(key, value) |
③ | 删除一个键值对 | del x[key] | x.__delitem__(key) |
④ | 为缺失键提供默认值 | x[nonexistent_key] | x.__missing__(nonexistent_key) |
def __getitem__(self, key):
if self.list is None:
raise TypeError('not indexable')
found = []
for item in self.list:
if item.name == key:
found.append(item)
if not found:
raise KeyError(key)
if len(found) == 1:
return found[0]
else: return found
可比较的类
“比较”操作并不局限于数字——字符串、列表,甚至字典。如果要创建自己的类,且对象之间的比较有意义,可以使用下面的特殊方法来实现比较。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 相等 | x == y | x.__eq__(y) |
② | 不相等 | x != y | x.__ne__(y) |
③ | 小于 | x < y | x.__lt__(y) |
④ | 小于或等于 | x <= y | x.__le__(y) |
⑤ | 大于 | x > y | x.__gt__(y) |
⑥ | 大于或等于 | x >= y | x.__ge__(y) |
⑦ | 布尔上上下文环境中的真值 | if x: | x.__bool__() |
☞如果定义了
__lt__()
方法但没有定义__gt__()
方法,Python 将通过经交换的算子调用__lt__()
方法。然而,Python 并不会组合方法。例如,如果定义了__lt__()
方法和__eq()__
方法,并试图测试是否x <= y
,Python 不会按顺序调用__lt__()
和__eq()__
。它将只调用__le__()
方法。
可序列化的类
Python 支持 任意对象的序列化和反序列化。(多数 Python 参考资料称该过程为 “pickling” 和 “unpickling”)。所有的内置数据类型 均已支持 pickling 。如果创建了自定义类,且希望它能够 pickle,阅读pickle 协议 了解下列特殊方法何时以及如何被调用。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 自定义对象的复制 | copy.copy(x) | x.__copy__() |
② | 自定义对象的深度复制 | copy.deepcopy(x) | x.__deepcopy__() |
③ | 在 pickling 之前获取对象的状态 | pickle.dump(x, file) | x.__getstate__() |
④ | 序列化某对象 | pickle.dump(x, file) | x.__reduce__() |
⑤ | 序列化某对象(新 pickling 协议) | pickle.dump(x, file, protocol_version) | x.__reduce_ex__(protocol_version) |
⑥ | 控制 unpickling 过程中对象的创建方式 | x = pickle.load(file) | x.__getnewargs__() |
⑦ | 在 unpickling 之后还原对象的状态 | x = pickle.load(file) | x.__setstate__() |
* 要重建序列化对象,Python 需要创建一个和被序列化的对象看起来一样的新对象,然后设置新对象的所有属性。__getnewargs__()
方法控制新对象的创建过程,而__setstate__()
方法控制属性值的还原方式。
可在 with
语块中使用的类
with
语块定义了 运行时刻上下文环境;在执行 with
语句时将“进入”该上下文环境,而执行该语块中的最后一条语句将“退出”该上下文环境。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 在进入 with 语块时进行一些特别操作 | with x: | x.__enter__() |
② | 在退出 with 语块时进行一些特别操作 | with x: | x.__exit__() |
以下是 with file
习惯用法 的运作方式:
# excerpt from io.py:
def _checkClosed(self, msg=None):
'''Internal: raise an ValueError if file is closed '''
if self.closed:
raise ValueError('I/O operation on closed file.' if msg is None else msg)
def __enter__(self):
'''Context management protocol. Returns self.'''
self._checkClosed()
return self
def __exit__(self, *args):
'''Context management protocol. Calls close()'''
self.close()
__enter__()
方法检查文件是否处于打开状态;如果没有,_checkClosed()
方法引发一个例外。__enter__()
方法将始终返回 self —— 这是with
语块将用于调用属性和方法的对象- 在
with
语块结束后,文件对象将自动关闭。怎么做到的?在__exit__()
方法中调用了self.close()
.
☞该
__exit__()
方法将总是被调用,哪怕是在with
语块中引发了例外。实际上,如果引发了例外,该例外信息将会被传递给__exit__()
方法。查阅With 状态上下文环境管理器 了解更多细节。
真正神奇的东西
如果知道自己在干什么,你几乎可以完全控制类是如何比较的、属性如何定义,以及类的子类是何种类型。
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
① | 类构造器 | x = MyClass() | x.__new__() |
② | 类析构器 | del x | x.__del__() |
③ | 只定义特定集合的某些属性 | x.__slots__() | |
④ | 自定义散列值 | hash(x) | x.__hash__() |
⑤ | 获取某个属性的值 | x.color | type(x).__dict__['color'].__get__(x, type(x)) |
⑥ | 设置某个属性的值 | x.color = 'PapayaWhip' | type(x).__dict__['color'].__set__(x, 'PapayaWhip') |
⑦ | 删除某个属性 | del x.color | type(x).__dict__['color'].__del__(x) |
⑧ | 控制某个对象是否是该对象的实例 your class | isinstance(x, MyClass) | MyClass.__instancecheck__(x) |
⑨ | 控制某个类是否是该类的子类 | issubclass(C, MyClass) | MyClass.__subclasscheck__(C) |
⑩ | 控制某个类是否是该抽象基类的子类 | issubclass(C, MyABC) | MyABC.__subclasshook__(C ) |
博客地址:http://www.cnblogs.com/wanghui1991
执行方式:Linux平台下安装好python3.x以上版本
chmod +x login.py
./login.py
数据:account.db村烦的事锁定的账户信息
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os, sys, getpass
# 定义变量
lock_file = 'lock.db' # 锁定用户信息
account_file = 'account.db' # 正确用户信息
counter = 0 # 初始化计数器
while counter < 3:
username = input('please input your name:').strip()
lock_check = open(lock_file, 'r+')
account_check = open(account_file, 'r+')
for u_line in lock_check.readline():
u_line = u_line.strip('\n')
if username == u_line:
sys.exit("%s is locked!!" % username)
password = getpass.getpass('please input your password:')
for p_line in account_check.readlines():
user, passwd = p_line.strip('\n').split()
if username == user and password == passwd:
sys.exit('user %s ,welcome to login!!' % username)
elif username != user:
sys.exit('username incorrect!!')
elif username == user and password != passwd:
print('retry again!!')
counter += 1
account_check.close()
else:
print('user %s is locked!!' % username)
lock_now = open(lock_file, 'a')
lock_now.write(username)
lock_now.write('\n')
lock_now.close()
运算符重载