re

Python对正则匹配的库是re,re是基于Perl所用的正则表达式,并有
一定的改进.

正则本质就是搜索所需的文本,正则里面有三种搜索方式

  1. 第一种是知道文本内容直接使用普通字符搜索出来,比如要从abcdefg中搜到cd
  2. 第二种就是模糊查询,比如我想从英文中找一个数字,一般借助特殊符号(.+*?)或者转义符号(\w\d等)
  3. 第三种就是结合前两种,比如我记得一个单词的前两个字母想把那个单词搜出来.

这里不介绍正则基本知识,你想知道可以点这里

ps: 由于在python里面也是用反斜杠做转义字符,所以比如\\\b这两个特殊字符必须用\\\\\\b来代替.但是python提供了一个元字符支持re模块,只要字符前面加上r比如r' regex '就能不关闭python的转义.

正则里面我觉得很重要的一个概念就是组概念,当我们的文本比较复杂的时候将其分成多个小组是利于我们正则的后期维护和改进

正则里面使用一个括号来表示组比如(a)(b)就分成了两个组

re函数里面searchfinall都支持组查询,而且findall方法假如里面有组分布会只显示组成员.

re库支持搜索选项,这几个选项对于正则有时候非常有用

DOTALL [简S]-------------允许点字符匹配换行符
IGNORECASE [简I] --------忽悠大小写
LOCALE  [简L]  ----------支持本地化字符
MULTILINE [简M] ---------多行,每行都支持锚点
UNICODE [简U]  ----------支持Unicode,\w也可以是Unicode了
VERBOSE  [简X]  --------------神器,会无视代码中的注释空格和换行

我们也可以在正则的组里面使用这些搜索选项,只要用上面的简称的小写比如(?is)就可以在组里面使用这些规则.


正则里面还有一些比较有趣的函数,同string里面的translate函数,sub函数可以替换找到的变量
bold = re.compile(r’*{2}(.?)\{2}’)
bold.sub(r’\1‘, ‘this foo and ok‘)

\1代表第一组变量也就是foo和ok
输出为'this <b>foo</b> and <b>ok</b>'我们使用成功用加粗了foo和ok,同translate不同这个方法不需要知道要替换的是什么.


正则的断言

我们可以使用一些特殊的符号来执行一些程序判断选择,比如说判断是否特殊字符,如果有
的话就不匹配,这就是断言

断言有两种一种是前向,一种是后向

前向是指判断语句在前面,这种就相当于一个if语句,而后向是匹配后判断,由于已经匹配好了文字所以
匹配的字符必须是固定长度的(不能使用*.?).

前向就是在判断后面匹配的表达式必须与规定相同,比如一个邮箱地址我们要匹配可以用<>包起来的,但是不匹配只要一个的我们就可以在前面加上这个^(?=(<.*>$)|([^<].*[^>]$))通过使用?=来断言后面必须是用<>包起来或者没有<>,我们使用前向断言可以通过正则直接过滤掉不符合的(当然你可以用多个简单正则来做但是效率没有这个高),还有否定前向就是通过?!来声明.
相对应后向断言就是很简单了,直接在匹配后面使用一个?<=(肯定后向)或?<!(否定后向),不过要注意这个是判断前面匹配是否满足的.

断言只是限定我们想选的文本的范围,他并不会被选择.
断言的一个有趣的应用就是选择字符间的空格,我们知道python其实假设每个字符间都一个空格(这就是我们有时候会选出一些空字符出来的原因),这个空格不是我们自己打上去的.

举个例子

两个字符串a1a 1,第一个我们称为A,第二个我们称他为B,假如我们想把数字和字母分出来,对于B来说,很简单因为数字和字母之间有一个空格,我们可以直接使用字符自带的split就行,但是对于A来说,就不那么简单了.

字母a和数字1中间没有字符,我们必须把字母和数字之间的”空格”给选择出来,这时候就可以用到断言了.

r = re.compile(r'(?<=[a-z])(?=\d)') 

这个r就可以字母和数字直接的隐形空格给选择出来了

遗憾的是由于python的正则并不把隐形的空格当做字符,所以我们不能简单的使用正则的r.split方法(选择字符分割)直接将字符串分解开.

我们就得写几步

第一先把空格换成 $$$(或其他)

>>> s = r.sub('$$$', 'a1')
>>> print(s)
'a$$$1'

然后在分割

>>> s.split('$$$')
>>> ['a', '1']

成功分割好了,当然这个只能处理字母在前数字在后的”隐形空格”,只要加一个"|"在把前向改成后向,后向改成前向就可以选择任意字母和数字直接的”隐形空格”了.


正则的变量

我们可以使用?P来声明一个组(用括号,当然其实我们每使用一个括号re自动帮我们将组取一个
名,依次从1-n

有时候我们可以要求上面的匹配组,下面也要相应匹配组,我们就可以通过两种方法来引用这个变量,假如你没有使用<?P<name>来声明组你只能通过\n来引用,n是这个变量的序号,第二种是通过(?P=name)
来引用这个变量,name为你自己定义的组的名字

re还提供了一种机制来让你修正你的正则,简单来说就是能判断一个组存不存在来约束匹配,语法为

(?(id)yes-expression|no-expression)

id为组的编号或者name.

管线命令 (pipe):

  1. 撷取命令: cut, grep
  2. 排序命令: sort, wc, uniq
  3. 双向重导向: tee
  4. 字符转换命令: tr, col, join, paste, expand
  5. 分割命令: split
  6. 参数代换: xargs

    分割文档

长长的一大片文档有时我们并不愿意看到全部内容,我们只想关注
部分内容的时候了可以考虑使用分割文档命令

cut是一个很好的分割文档工具

vi常用命令

  1. [Ctrl] + [u] 屏幕『向上』移动半页
    • 光标移动到非空格符的下一列
    • 光标移动到非空格符的上一列
  2. n那个 n 表示『数字』,例如 20 。按下数字后会向右移动这一行的n 个字符。例如 20<spac移动 20 个字符距离。
  3. 0 这是数字『0 』:移动到这一行的最前面字符
  4. $ 移动到这一行的最后面字符处(常用)
  5. H 光标移动到这个屏幕的最上方那一行
  6. M 光标移动到这个屏幕的中央那一行
  7. L 光标移动到这个屏幕的最下方那一行
  8. G 移动到这个档案的最后一行(常用)
  9. nG n 为数字。移动到这个档案的第 n 行。例如 2档案的第 20 行(可配合 :set nu)
  10. gg 移动到这个档案的第一行,相当于 1G 啊! (
    n n 为数字。光标向下移动 n 行(常用)

泛型对于解决面对对象编程的算法设计可以提高其运算速度,但是对于引用类型来说还是没什么差别,因为引用类型只是指针的地址的调用,简单来说泛型还是挺好理解的,但是对于泛型、非泛型、继承和接口的融合就有些迷惑了。

比如说这种接口
public interface IEnumeratot<T>:IDisposable,IEnumerator,ICompare<T>

这个泛型接口继承了两个非泛型接口,和一个泛型接口。


我一开始理解泛型就是一个个模型,只要我们把类型一个参数赋给他,他就能生成一个标准的类型,他缺少的只是一个参数而已,我们引用的时候感觉就像我们引用一个“全体方法”,把参数赋给类型后就可以一直调用类中的方法了,但是对于接口的继承如何理解?

对于泛型类的继承,继承的类必须实现泛型的参数或者保留泛型的参数,比如下面

  public class A<T>
{
    public T tt;
}
public class C<T> : A<T>
{
    public T tt;
}

或者是这样

  public class A<T>
{
    public T tt;
}
public class C : A<string>
{
    C cc;
}

对于泛型继承非泛型类,比如下面

public class A 
{
     A aa;
 }

 public class B<T>:A
{
    B<T> bb;
}

基类是非泛型,而继承的是泛型类,我感觉这种构造就是让泛型类多了一种包容性,比如下面的链表实现的代码,让基类是非泛型,而继承是泛型,就能让链表可以连起很多种类型的数据,而本身的类型安全没有丢失。

public class Node
{
        pretected Node next;
          public Node(Node next){
            this.next=next;
            }
}
public class TypeNode<T>:Node
{
    public T data ;
    public TypeNode(T data):this(data ,null){
        }
    public TypeNode(T data,Node next):base(next){
        this.data=data;
        }

    }

泛型的约束

GitHub推出一个对学生和教师的福利包,对于学生来说这是一个不小
的福利,只要通过一个edu邮箱就可以领取,但奈何国内有些无良人买卖
邮箱,所以GitHub对于.cn的邮箱一律拒绝,但是可以通过上传学生证的方法
得到验证,题主刚开始用学校邮箱试了试,失败了,抱着试一试的心态,上传了
学生证,没想到第二天就给我回复,并给我这个豪华大礼包,接下来我就介绍介绍
如何用这个包来.

有些人在网上说,上传学生证没有用,可能是那个plan(GitHub会叫你写一点你想用GitHub做什么)用的是中文写的,最好用英文写-_-,回复的会快一点.

Digital Ocean —VPS 50刀

以前貌似是100刀,现在缩水一半了,不知道为什么.

DigitalOcean是一家以优质的VPS服务器著名,毕竟用SSD做存贮的服务器商没几家.

这个是包小时的我们可以最便宜的5刀每月,提供20GSSD,1TB流量,我们可以用它来搭建服务器或者搭建一个shadowsocks服务器,安装shadowsocks很简单,但是怎么得到这50刀就要花点时间了.

你要是想得到这50刀必须先充值5刀,但是怎么给钱有是个问题,真是有钱也花出去啊.你可以选择绑卡,但是很复杂不一定能绑的上,最好的方式使用PayPal付这5刀,PayPal可以绑定银联卡付款,但是这个PayPal注册又是个问题,当时题主注册的时候一直提示服务器故障.

当时去上网搜了搜,中国大陆是有这个情况,可以通过贝宝(PayPal在中国的分公司)来注册

提醒一句,绑银联卡的时候最好用IE来绑定,别问我为什么谷歌浏览器不行—-

选择VPS的时候推荐San Francisco,延迟最低.

搭建shadowsocks可以参考这篇博客

NameCheap

ME域名一个(一年,价值8.99刀)PositiveSSL一个 (一年,价值9刀)

这个要想得到域名必须通过邮箱验证还好NameCheape承认.edu.cn邮箱

提醒一下,通过DVC验证的时候选择邮箱验证就够了.虽然不是你的邮箱但是会把资料发到你提供的邮箱

可以参考这篇博客搭建你的https网站,本站也是采用这种方法搭建的.但是有一点不同的时,现在NameCheap直接发给我一个.crt文件和.ca-bundle文件(用于Apache),所以把.key文件和.crt文件放到服务器上配置一下就好了.

GitHub Micro account 7刀/month

这个不错我们可以有五个私有项目,一直可以用到你毕业.

这个不错哦,妈妈再也不怕我写的stupid代码被人看到了 O(∩_∩)O哈哈~.

首先谈谈自己对EF的接触的过程吧,最先接触EF只是因为EF支持从数据库把关系扒下来,可以省掉自己写Select、Update、Insert这些SQL语句,而且修改非常方便,后来在使用的过程中发现导航属性这个关系,然后才慢慢知道数据库的索引是什么,由于自己接管的是大学生社团的数据库,大多时候创建者并不会考虑表的联系,一般创个主键就完事了(顺便吐槽一句,握草,数据库的表名和列名是什么鬼全用拼音首字母,为了兼容前面的内容我们还得花一半时间猜你们的列名,简直醉了,除了ID这个英文他们会,你们的英语是体育老师教的吗???)言归正传,用EF的确学到了对数据库表的的建立的理解,毕竟自己刚学数据库的时候就是把所有的字段塞到一张表里面,刚开始自己使用EF从数据库拔下来的表然后修改实体的关系的数据(感觉其实就是使用EF的EMDX的Code First),使用这个并没有出现很多问题,后来又接触完整的Code First,就是直接用代码生成数据库,虽然中间遇到无数的BUG但是这些BUG让我对数据库和EF的关系有了更深的理解,话不多说,直接上BUG。


1. EF未能确定外键,请用注解属性或Fluent API标记外键

网上关于如何用代码的(Fluent API或注解属性)指定外键的文章有很多有很多。在这里我想谈谈对外键的理解,首先建立起一张主表


主表














列名

类型

ID

int

Name

nvarchar(50)

首先ID是独一无二的,而Name不是(重名的有很多),当我们给ID套上主键的时候,这时候插入这张表的ID只能有一种(这是数据库的一种约束,当然你可以不选择这种约束),一个人除了姓名还有其他东西,假如这时我们还有帮他加入性别这个信息,我们可以修改上一张表添加一个字段,也可以新建一张表存贮性别这个信息(当然在实际生活中只用一张表存一个信息很少),我们新建的这张表是这样的,


附表










列名

类型

Sex

bit

这张表存贮了性别这个信息,但是如何将他从主表联系起来呢,我们先提取主表中的ID作为联系(我们称为外键)表改为


附表














列名

类型

ID

int

Sex

bit

我们把列名ID设为主键,这样我们就建立了一对一的关系,这个附表的ID必须不为空,这种关系还有一种就是将外键存贮在主表里面,就是将主表里面添加一个外键SexID,主表和附表要改成下面这种


主表




















列名

类型

ID

int

Name

nvarchar(50)

SexID

int

附表










列名

类型

Sex

bit

现在这种结构就是外键SexID可以为空(注上面的外键不能为空),

ps:说到外键不能为空我插一句,有些教科书上说外键不能为空也是对的,外键只是一个列名,当这个列名不唯一(也就是不为主键的时候)这是外键可以为空,为空的含义是不确定对应主表的值。

现在开始谈谈这种情况在EF发生的原因,你吧主表设为Person表,附表为SexInfo表,对应的代码如下

public Person{
    public int ID{get;set;}
    public string name{get;set;}
    public virtual SexInfo Sex{get;set;
                                            }


public SexInfo{
    public int ID{get;set;
    public bool Sex{get;set;
    public Person person{get;set;}
                            }

这个时候EF无法判断哪个是主表那个是附表,就是无法将外键加在哪个表的ID上,或者像上面的表中在Person表中添加一个外键。也就是在这种情况里面有四种可能的情况

  1. 在Person表里面添加一个外键(假设为Person_SexInfoID)
  2. 将Person表中的ID设为主键和外键
  3. 在SexInfo表中添加一个外键(假设为SexInfo_PersonID)
  4. 将SexInfo表中的ID设为主键和外键。
注假设在EF中没有给属性添加[Key]注解属性或在Fluent API中声明一个属性为主键的话,EF会自动将有ID后缀的属性设置为主键并让他为标志字段自增,还有表中没有主键无法导入到EF中。

虽然EF有自动检测代码生成关系,但是本人还是比较推崇自己在Code First时就想好外键,这样在用模型绑定的时候就不会发生一些很可能发生的错误。在这张表里面为了节约数据库空间最好在SexInfo里面添加一个外键,现在我就来谈谈分别在两个表里面添加外键可能会遇到的BUG。

  1. 在SexInfo里面添加外键PersonID

类修改成为

public Person{
    public int ID{get;set;}
    public string name{get;set;}
    public virtual SexInfo Sex{get;set;
                                            }


public SexInfo{
    public int ID{get;set;
    public bool Sex{get;set;
    public int PersonID{get;set;}
    public Person person{get;set;}
                            }

然后我们可以选择在PersonID上加上[ForeignKey("Person")][Requird],或者在重写的OnModelCreating方法中加入 这样一句代码

modelBuilder.Entity<SexInfo>().HasRequired(x => x.Person)
.WithRequiredPrincipal(x => x.BindingRole).HasForeignKey(x => x.MenusManageID)        

其实我更推崇写Fluent API 来约束,因为将注解属性放在Model里面太乱而且容易错,比如说假如你在PersonID上面少注释了一个[Required] 你又会得到一个模型验证错误,这个BUG是隐藏的最深的,现在来重点提一提这个BUG

BUG:模型验证错误····多重性与关系“········”中 Role“··············”中的引用约束冲突。因为 Dependent Role 中的所有属性都不可以为 null,Principal Role 的多重性必须为“1”。

里面值类型不能为空(如果没有初始化时为0),所以EF报错,你要么给外键加上Required标记指定它必须存在,要么给一个可为空的int型,像这个示例里面外键PersonID是必须的,然后有些对应是0-1 对 1,所以这时候就疑惑了我们怎么给外键赋值,我们有一种办法命名一种类型他的值可以int也可以为空,但是EF会认识我们这种独特的外键吗?还好EF早想到了这点,有一种泛型可以为空也可以为你想要的类型,这种就是Nullable<T> ,在这个方法中我们只要将外键PersonID的类型换成 这个

public Nullable<int> PersonID{get;set;}

自己本身与数据库类型的对应,C

还有一个比较常见的BUG吧,来提一提。

BUG:······: 引用约束的 Dependent Role 中所有属性的类型都必须与 Principal Role 中相应的属性类型相同。引用约束“·····”中,实体“····”的属性“····”的类型与实体“·····”的属性“·····”的类型不匹配。

这个bug就是相对应主体和外键不匹配的情况,相对应的类如下

    public Person{
    public long ID{get;set;}
    public string name{get;set;}
    public virtual SexInfo Sex{get;set;
                                            }


public SexInfo{
    public int ID{get;set;
    public bool Sex{get;set;
    public int PersonID{get;set;}
    public Person person{get;set;}
                            }

Person里面的主键我改成了long型,然而外键PersonID却是int型,出现这个错误是对外键的认识还不够,外键其实就是主键的“分身”,主键是long型,外键必须也是long型,同理主键是int型外键也必须是ing型,

ps:导航属性是指对象,比如说Person类实例person,而外键是指存贮在数据库里面的一个特殊的列名。


充分认识导航属性和外键是搭建一个扎实的数据库结构的基础,在学习和应用EF的过程中也是了解数据库的结构的学习过程,EF或许在运行速度方法上比一般的SQL语句要慢,但是用EF我们可以更加方便的搭建一个好的数据体系,搭建一个好的数据体系可以让你在完成项目的时候事半功倍。

pandas的基本数据结构是DataFrame和Series,DataFrame可以看做是一张表
,Series可以看做一个有序队列,我们队数据的处理就通过操作者两个东西来
实现的

对于

由于有其他编程语言基础,所以对于python的学习并不吃力,但是整体感觉python的确与前面学习c、c———

1. 实时编译VS静态编译

不需要输入任何前缀,直接将代码放在python解释器上面就能运行,虽然window下不支持直接点开文件就能使用,但是只要安装了python解释器就能很轻松的运行。

分量轻是他的特点吧!相比打开vs等半天然后,编译连接最后执行。python是一门很轻巧的语言,没有满屏的分号,大括号,基本类型比如int、string、float不区分直接拿来用就可以了,任何一个变量都是一个对象,对象可以千变万化,感觉python是一门很野的熊孩子什么都不在乎,比如说你什么了相同的两个变量

这门强类型语言则不允许,在同级作用域内他只允许声明一次,python或许已经没有声明了,每个名字只是一个对象而已并没有他的归属。

2.动态语言VS静态语言

给我感受是一颗静止的树的话,那么python就是一匹‘野马’,C

最近在学利用python进行数据分析,发现pandas这个库对数据的处理非常灵活,
而且号称python的maltlab的.其在图形绘制也非常方便

在Windows下安装python关于科学计算的库非常不方便,还好有一些公司做好了关于python计算库在Windows安装的安装文件,这里我推荐anaconda,我推荐安装32位包,因为64位有很多库不支持.

pandas内部是使用numpy做存贮结构的,所以我们必须先了解一下numpy

下面是numpy的简析

关于pandas的DataFrame和Series的理解

numpy简单来说就是python的C版数组实现,因为python原生列表虽然好使,但是生成大量数据时开销很大,而numpy是基于C的,生成大量数组非常简单,而且操作他们速度非常快.

由于numpy是基于C的,所以numpy是一种强类型的,当然numpy是可以判断数组里面是数据类型,但是我们可以显示声明他们,dtype是声明的参数,一般我们通过下面的方式简单声明一个narray

import numpy as np
arr = np.array([1, 2, 3], dtype=np.int32)

numpy还有一个强大的地方是多维数组,numpy对多维数组的支持很好.只要简单的使用嵌套序列就能被转化成多维数组.
比如
arr2 = np.array([[1, 2], [3, 4]])

numpy另一个强大的地方是矢量化,这对于科学计算来说非常有用

比如前面的arr,
我们可以简单使用

arr3 = arr * arr

得到另外一个序列 [1, 4, 9],数组间的运算应用到了元素级.

numpy之所以成为数据分析的基本数据结构,还在用取数据的灵活性

对于一维数组来说,python自身的列表就支持切片处理,numpy不仅支持切片处理,还支持列表取出,比如:

>>> num =  np.arange(10)
>>> num[[3, 1, 0]]
array([3, 1, 0])
在一维数组里面这个并没有什么优势,因为我们可以通过数据简单一个构造器[num[x] for x in [3, 1, 0]]构造出来.

当但在多维数组我们使用构造器非常繁琐了, numpy使用了很多技术使我们很方便的取出多维数组

我们先创建一个多维数组

arr = np.arange(32).reshape((8, 4))

生成的arr是

array([[ 0,  1,  2,  3],
   [ 4,  5,  6,  7],
   [ 8,  9, 10, 11],
   [12, 13, 14, 15],
   [16, 17, 18, 19],
   [20, 21, 22, 23],
   [24, 25, 26, 27],
   [28, 29, 30, 31]])

现在像一维数组一样的取出数据

In[13]: arr[[0, 3]]
Out[13]: 
array([[ 0,  1,  2,  3],
   [12, 13, 14, 15]])

我们取出了一个二维数组

试试用这个

In[14]: arr[arr > 8]
Out[14]: 
array([ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
   26, 27, 28, 29, 30, 31])

我们取出一个一维数组,这个arr > 8 是也是一个二维数组对应原来数组的每个位置都有一个布尔值代替,当numpy判断得到是一个array数组时会广播每个值来判断是否获取,这个可比你用for循环快了很多.

array([[False, False, False, False],
   [False, False, False, False],
   [False,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True]], dtype=bool)
numpy很善于处理不同的选择,当你直接给一个数组时,如上面的arr[[0, 3, 4]],它默认第二维为全部选择也就是想当与arr[[0, 3, 4], :],当你给确定的值时,他就会在第二维上取响应的值,比如
arr[[0, 3, 4], 1] 或 arr[[0, 3, 4], [1, 1, 1]]

从上面你可以看到,如果第二维你每个都想取第二个,你可以直接写一个整数就行,numpy会广播过去,假如想你分别再第一维的每个上分别对应取哪个你就可以用数组来分别选择.

有时候我们想在二维数组上面取出一个矩形块,直接使用 arr[[0, 3, 4], [1, 2, 3]]只能取出二维数组矩形块的对角线,我们这时候就可以先取出第一维的矩形列,然后再在取出的列中取出矩形行
arr[[0, 3, 4]][:, [1, 2, 3]]

当然我们还可以用二维数组来取出数据
arr[[[0], [3], [4]], [[1, 2, 3]]]

[[0], [3], [4]]代表第一维的1, 4, 5列, [[1, 2, 3]]代表第二维的2, 3, 4行.

numpy给我们提供一个函数将一维数组转换成二维数组我们可以简单使用
np.ix_([0, 3, 4], [1, 2, 3])

生成二维数组,这样我们使用

arr[np.ix_([0, 3, 4], [1, 2, 3])]

就可以取出矩形块了.

Pillow 是 PIL的对Python3支持的另外一个分支,当然他对Python2也兼容,由于PIL安装起来比较烦,而使用pip可以很轻松的安装Pillow,所以我选择Pillow使用,但是其核心还是PIL库的。


Python的图形处理库如PIL一直很强大,但是要想使用好它必须对图片有一定的知识储备。
使用起来很简单

from PIL import Image

引用Image包

im = Image.open('1.png')

打开图片,得到一个im对象,我们接下来就可以对这个对象进行操作(前提有这个1.png图片)

我们先看一下他的一些属性

>>> print im.format, im.size, im.mode
PNG (83, 81) RGB

第一个我们输出图片的格式,图片有很多种格式,常用的有jpg、png还有gif动图啊,PIL支持很多种格式,我们可以使用PIL轻松的将格式转换,im.save('1.jpg'),当然你可以选择格式假如你没选好后缀名的话,im.size就是图片大小,他返回的是一个元组第一个长度第二个是宽度,单位是像素。
现在就谈谈 这三个属性对应的关系吧
首先我们使用一张像素图来说吧

我们存贮图片的时候是将整个图像分成很多个相同的小方块,每个小方块我们称为像素,当然一张图片分的越小,像素越多,那么图片就越接近真实图片,上面的im.size属性就告诉我们,这张图片分成了,长为83px,宽为81px的图片,那么一共有83*81=6723个像素点,每个像素点里面存什么呢,这就是im.mode属性告诉我们的,贴一下属性有什么吧

  1. 1 (1-bit pixels, black and white, stored with one pixel per byte)
  2. L (8-bit pixels, black and white)
  3. P (8-bit pixels, mapped to any other mode using a color palette)
  4. RGB (3x8-bit pixels, true color)
  5. RGBA (4x8-bit pixels, true color with transparency mask)
  6. CMYK (4x8-bit pixels, color separation)
  7. YCbCr (3x8-bit pixels, color video format)
  8. I (32-bit signed integer pixels)
  9. F (32-bit floating point pixels)

像素存贮就是涉及到颜色的存贮,在早期的黑白游戏机,只有黑和白两种,那么每个像素点就只有1位颜色来存贮,1位只能存贮两种颜色,八位色就能存256种颜色,像八位我们能用256个油漆桶/256色调色板来形容,像上面我们使用的RGB是由三种三原色红绿蓝混合而成,我们知道大自然所有的颜色都可以用红绿蓝三种颜色调配出来,所以RGB又被称为真彩(true color),每种颜色我们都分成256种,所以我们一共有256256256=16777216种颜色可以调配,像素的其他模式我们不介绍太多,有兴趣的可以自己钻研。
那么我们知道每个像素占多少字节,又知道共有多少个像素,那我们是不是就可以直接计算出来图片大小,来验证一下

以第一张图片为例,共有8381=6723个像素点,用RGB模式,每个像素三个字节,共有67233=20667b=20kb,但是我这张图片只有11.6kb,误差太大了吧,这时候我们就要介绍一下上面那个im.format属性了,这张图片采用png格式,我们先尝试一下把他转成JPG格式吧

im.save('1.jpg')

我们再查看一下这个1.jpg的大小,只有2.24kb了,我们用PIL打开这张图片

>>> im2 = Image.open('1.jpg')
>>> print im2.format, im2.size, im2.mode 
JPEG (83, 81) RGB

图片大小没有改变,但是format变成了JPEG,而且文件大小变成原来的1/5,
JPEG和GIF和PNG是三种图片压缩技术,他们使用压缩算法把图片压缩成很小,当我们打开图片时,解密算法把他还原出来,所以我们算出来的大小与压缩后的大小是不一样的。
有了这些概念我们就能更好的使用PIL提供给我们的magic方法,下次在谈我对PIL的高级应用吧。