Python 笔记

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。

  1. 先引入numpy。

  2. 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.__bases__

(<type 'object'>,)

其实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)
  1. 对 __init__() 方法的调用发生在实例被创建 之后 。如果要控制实际创建进程,请使用 __new__() 方法
  2. 按照约定, __repr__()方法所返回的字符串为合法的 Python 表达式。
  3. 在调用 print(x) 的同时也调用了 __str__()方法。
  4. 由于 bytes 类型的引入而从 Python 3 开始出现。

行为方式与迭代器类似的类

序号目的所编写代码Python 实际调用
遍历某个序列iter(seq)seq.__iter__()
从迭代器中获取下一个值next(seq)seq.__next__()
按逆序创建一个迭代器reversed(seq)seq.__reversed__()
  1. 无论何时创建迭代器都将调用 __iter__()方法。这是用初始值对迭代器进行初始化的绝佳之处。
  2. 无论何时从迭代器中获取下一个值都将调用 __next__()方法。
  3. __reversed__() 方法并不常用。它以一个现有序列为参数,并将该序列中所有元素从尾到头以逆序排列生成一个新的迭代器。

计算属性 

序号目的所编写代码Python 实际调用
获取一个计算属性(无条件的)x.my_propertyx.__getattribute__('my_property')
获取一个计算属性(后备)x.my_propertyx.__getattr__('my_property')
设置某属性x.my_property = valuex.__setattr__('my_property',value)
删除某属性del x.my_propertyx.__delattr__('my_property')
列出所有属性和方法dir(x)x.__dir__()
  1. 如果某个类定义了 __getattribute__() 方法,在 每次引用属性或方法名称时Python 都调用它(特殊方法名称除外,因为那样将会导致讨厌的无限循环)。
  2. 如果某个类定义了 __getattr__() 方法,Python 将只在正常的位置查询属性时才会调用它。如果实例 x 定义了属性color,x.color 将 不会 调用x.__getattr__('color');而只会返回x.color已定义好的值。
  3. 无论何时给属性赋值,都会调用 __setattr__()方法。
  4. 无论何时删除一个属性,都将调用 __delattr__()方法。
  5. 如果定义了 __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]))  
  1. _ZipDecryptor 类维护了以三个旋转密钥形式出现的状态,该状态稍后将在 _UpdateKeys()方法中更新(此处未展示)。
  2. 在此例中,__call__()对 zip 文件的单个字节进行解密,然后基于经解密的字节对旋转密码进行更新。
  3. 给出 zip 文件的头 12 个字节,将这些字节映射给 zd 进行解密,实际上这将导致调用 __call__() 方法 12 次

行为方式与序列类似的类

如果类作为一系列值的容器出现。

序号目的所编写代码Python 实际调用
序列的长度len(seq)seq.__len__()
是否包含特定的值x in seqseq.__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” 运算符了。

  1. 而 __contains__()方法是令该魔法生效的主角。
  2. if 'q' in fs,Python 将在 fs 对象中查找 __contains__() 方法。'q' 的值被当作key 参数传入__contains__()方法。
  3.  len(fs) 调用FieldStorage 的__len__()方。
  4. self.keys() 方法检查 self.list is None 是否为真值,因此 __len__ 方法无需重复该错误检查。

行为方式与字典类似的类

在前一节的基础上稍作拓展,就不仅可以对 “in” 运算符和 len() 函数进行响应,还可像全功能字典一样根据键来返回值。

序号目的所编写代码Python 实际调用
通过键来获取值x[key]x.__getitem__(key)
通过键来设置值x[key] = valuex.__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 == yx.__eq__(y)
不相等x != yx.__ne__(y)
小于x < yx.__lt__(y)
小于或等于x <= yx.__le__(y)
大于x > yx.__gt__(y)
大于或等于x >= yx.__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()  
  1. __enter__() 方法检查文件是否处于打开状态;如果没有, _checkClosed()方法引发一个例外。
  2. __enter__() 方法将始终返回 self —— 这是 with语块将用于调用属性和方法的对象
  3. 在 with 语块结束后,文件对象将自动关闭。怎么做到的?在 __exit__() 方法中调用了 self.close() .

☞该 __exit__() 方法将总是被调用,哪怕是在 with 语块中引发了例外。实际上,如果引发了例外,该例外信息将会被传递给__exit__() 方法。查阅With 状态上下文环境管理器 了解更多细节。

真正神奇的东西

如果知道自己在干什么,你几乎可以完全控制类是如何比较的、属性如何定义,以及类的子类是何种类型。

序号目的所编写代码Python 实际调用
类构造器x = MyClass()x.__new__()
类析构器del xx.__del__()
只定义特定集合的某些属性 x.__slots__()
自定义散列值hash(x)x.__hash__()
获取某个属性的值x.colortype(x).__dict__['color'].__get__(x, type(x))
设置某个属性的值x.color = 'PapayaWhip'type(x).__dict__['color'].__set__(x, 'PapayaWhip')
删除某个属性del x.colortype(x).__dict__['color'].__del__(x)
控制某个对象是否是该对象的实例 your classisinstance(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()

 

 

 

 运算符重载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值