蒋海云个人博客,日拱一卒。 2017-07-21T15:40:28+08:00 jiang.haiyun#gmail.com CSAPP3 2.3 整数运算 2017-07-21T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/CSAPP3-2.3_integer_arithmetic 无符号数加法

w 位的两个无符号数相加,可能要用 w+1 个位来表示,此时会出现溢出。

对满足 $0 \leq x, y < 2^w$ 的 $x$ 和 $y$,有:

检测无符号数加法中的溢出:

对在范围 $0 \leq x, y \leq UMax_w$ 中的 x 和 y,令 $s = x +_w^u y$,则当且仅当 s < x (或者等价地 s < y) 时,发生了溢出。即因为 x 和 y 都非负,故 $x + y \geq x$,因此如果 s 没有溢出,就能够肯定 $s \geq x$,另一方面,如果 s 确实溢出了,就有 $s = x+y-2^w$,而 $y < 2^w$,此时 $s < x$

练习题 2.27 p98

写出如下原型的函数,当参数 x 和 y 相加不会产生溢出时返回 1.

/* Determine whether arguments can be added without overflow */
int uadd_ok(unsigned x, unsigned y)
{
    unsigned s = x + y;
    return s >= x;
}
*/

无符号数的求反

对满足 $0 \leq x < 2^w$ 的任意 $x$,其 $w$ 位的无符号逆元 $-_w^ux$ 为:

也就对于非零的数,求反即为二进制时的取反 + 1 再截断。

练习题 2.28 p98

假设 w=4,填写下面无符号加法逆元的位表示。

$x$ hex $x$ dec $-_4^ux$ dec $-_4^ux$ hex
0 0 0 0
5(0101) 5 11 B
8(1000) 8 8 8
D(1101) 13 3 3
F(1111) 15 1 1

补码加法

两个补码有符号数相加后,结果太大(正溢出)和太小(负溢出)时都会溢出。

对满足 $-2^{w-1} \leq x, y \leq 2^{w-1} - 1$ 的整数 x,y,有:

补码加法溢出情况

实现上,补码加法和无符号数的加法在位模式上是一样的,用的是相同的机器指令,只是对结果的解析不同。因此,计算时,只需进行二进制加法再进行截断即可。

补码加法的溢出检测:

对满足 $TMin_w \leq x, y \leq TMax_w$ 的 x 和 y,令 $s=x +_w^t y$。当且仅当 $x>0, y>0$,但 $s \leq 0$ 时,发生正溢出。当且仅当 $x<0, y<0$,但 $s \geq 0$ 时发生负溢出。

练习题 2.29 102

w=5 时填写下表:

x | y | x+y | $x+_5^ty$ | 情况 ——–| [10100] | [10001] | [100101] | [00101] | 负溢出,情况 1 [11000] | [11000] | [110000] | [10000] | 负值,正常,情况 2 [10111] |[01000] | [11111] |[11111] |负值,正常,情况 2 [00010] | [00101] | [00111] | [00111] | 正值,正常,情况 3 [01100] | [00100] | [10000] | [10000] | 正溢出,情况 4

练习题 2.30 p101

/* Determine whether arguments can be added without overflow */
int tadd_ok(int x, int y)
{
    int s = x + y;
    if ((x>0 && y>0 && s<=0) || (x<0 && y<0 && s>=0))
        return 0;
    return 1;
}

/*or*/
int tadd_ok(int x, int y)
{
    int s = x + y;
    int neq_over = x<0 && y<0 && s>=0;
    int pos_over = x>0 && y>0 && s<=0;
    return !neg_over && !pos_over;
}

练习题 2.31 101

查找下面的补码相加溢出判断的实现函数的 BUG:

/* Determine whether arguments can be added without overflow */
/* WARRING: This code is buggy */
int tadd_ok(int x, int y)
{
    int sum = x+y;
    return (sum-x == y) && (sum-y ==x);
}

模数加法形成了阿贝尔群(Abelian Group),因此代码中的判断,无论溢出与否,都是成立的,都返回 1;

练习题 2.32 p101

假设要写函数 tsub_ok,如果计算 x-y 不产生溢出,返回 1。

/* Determine whether arguments can be subtracted without overflow */
/* WARNNING: This code is buggy. */
int tsub_ok(int x, int y){
    return tadd_ok(x, -y);
}

x 和 y 取什么值是,会产生错误的结果?

当 $y=TMin=-2^{w-1}$ 时,$-y=y$,此时对于 tadd_ok(x, -y) 来说,当 x 为负时都认为溢出,返回 0,而 x 为非负时,都认为没有溢出返回 1。而情况恰恰相反,tsub_ok(x, TMin),当 x 为 负数时,应认为没有溢出,返回 1,而 x 为非负时,应认为溢出返回 0.

补码的非

因 $TMin_w + TMin_w = -2^{w-1} + (-2^{w-1}) = -2^w$,溢出后的结果为 0,故:

对满足 $TMin_w \leq TMax_w$ 的 x,其补码的非 $-_w^tx$ 为:

练习题 2.33 p102

w=4 时,补全下表中补码的加法逆元。

x(hex) x(dec) -x(dec) -x(hex)
0(0000) 0 0 0
5(0101) 5 -5 B
8(1000) -8 8 8
D(1101) -3 3 3
F(1111) -1 1 1

结合题 2.28,补码和无符号数的逆元(非、取反)产生的位模式是相同的,都是位模式取反+1,即对于任意整数 x,-x 和 ~x+1 的结果都是相同的。

如上面的方法类似,计算补码非的第二种方法是建立在向量分为两部分的基础上。假设 $k$ 是最右边的 1 的位置,因而 $x$ 的位级表示形如 $[x_{w-1}, x_{w-2},\cdots,x_{k+1},1,0,\cdots,0]$(只要 $x \neq 0$ 就能找到这样的 $k$),这个值的非写成二进制就是$[\neg x_{w-1}, \neg x_{w-2},\cdots,\neg x_{k+1},1,0,\cdots,0]$。

无符号数乘法

范围 $0 \leq x, y \leq 2^w -1$ 内的整数 x 和 y,乘积 $x\bullet y$ 的取值范围为 0 到 $(2^w-1)^2$,可能需要 2w 位来表示。但最终结果需要截断,故:

对满足 $0 \leq x, y \leq UMax_w$ 的 x 和 y 有:

补码乘法

补码乘法和无符号数乘法,由于结果存在截断的情况,它们的截断后的结果在位级上都是相同的,因此用的相同的机器指令。因此可能理解为它们在位级操作上是相同的,只是解析不同。

练习题 2.34 p104

填写下面 3 位数字的乘法结果。

模式 x y x.y 截断的 x.y
无符号 4[100] 5[101] 20[10100] 4[100]
补码 -4[100] -3[101] 12[01100] -4[100]
无符号 2[010] 7[111] 14[1110] 6[110]
补码 2[010] -1[111] -2[110] -2[110]
无符号 6[110] 6[110] 36[100100] 4[100]
补码 -2[110] -2[110] 4[100] 4[100]

练习题 2.35 p104

下面是判断补码乘法是否溢出的代码:

/* Determine whether arguments can be multiplied without overflow */

int tmult_ok(int x, int y)
{
    int p = x*y;
    /* Either x is 0, or dividing p by x gives y */
    return !x || p/x == y;
}

见题 2.31,我们不能用减法来检验加法是否溢出,但这里可以用除法为检验乘法是否溢出。

按照下面的思路,用数学推导来证明你的方法是对的。首先,证明 $x=0$ 时显然是正确的。另外,考虑 $w$ 位数字 $x(x \neq 0), y, p, q$,这里 $p$ 是 $x$ 和 $y$ 补码乘法的结果,而 $q$ 是 $p$ 除以 $x$ 的结果。

1、 $x$ 和 $y$ 的乘积可写成:$x \bullet y = p + t2^w$,其中,$t \neq 0$ 当且仅当 $p$ 的计算溢出。

2、而 $p$ 也可以写成这样的形式: $p=x \bullet q + r$,其中 $ r < x $。

3、故 $x \bullet y = p + t2^w = x \bullet q + r + t2^w$,故当且仅当 $r=t=0$ 时有 $q=y$。 待推导

练习题 2.36 p105

对于数据类型 int 为 32 位的情况,设计一个版本的 tmul_ok 函数,使用 64 位的 int64_t, 而不使用除法。

int tmul_ok(int x, int y)
{
    /* Compute product without overflow */
    /* 这一行的强制类型转换至关重要。如果写成 int64_t p = x*y;
    就会用 32 位值来计算乘积(可能溢出),再符号扩展到 64 位*/
    int64_t p = (int64_t) x * y;
    
    /* See if casting to int preserves value */
    return (int)p == p;
}

练习题 2.37 p106

XDR 库代码中存在乘法溢出的漏洞:

int ele_cnt;
size_t ele_size;

void *result = malloc(ele_cnt * ele_size);

当 ele_cnt 为 $2^{20}+1$,ele_size 为 $2^{12}$ 时,相乘结果溢出后值为 $2^12=4096$,导致分配的空间不够,复制时会越界。

当数据类型 int 和 size_t 都是 32 位时,你决定将待分配字节数设置为数据类型 uint64_t,来消除乘法溢出的可能性。你把原来对 malloc 调用替换为:

uint64_t asize = ele_cnt * (uint64_t) ele_size;
void *result = malloc(asize); /* malloc 的参数类型为 size_t */

A. 这段代码对原始的代码有了哪些改进?

B. 你该如何修改代码来消除这个漏洞?

改进:保存了乘积的未截断结果,可进一步进行是否溢出判断。

消除漏洞:

uint64_t required_size = ele_cnt * (uint64_t) ele_size;
size_t request_size = (size_t)required_size;
if (request_size != required_size) {
    /* Overflow must have occurred. Abort Operation */
    return NULL;
}
void *result = malloc(request_size);

乘以常数

移位和加减操作比乘法操作快的多,因此编译器常将乘法优化为移位和加减操作。

乘以 2 的幂

无论无符号数,还是补码,由于位级上的表示和操作相同,当乘以 $2^k$ 时,都是进行左移 $k$ 位操作(可能溢出)。

练习题 2.38 p107

LEA 指令能执行形如 (a<<k)+b 的计算,这里 k 等于 0, 1, 2, 或 3,而 b 等于 0 或某个程序值 。编译器常用该指令执行常数因子乘法。例如可用 (a<<1)+a 来计算 3*a

考虑 b 等于 0 或者 a,k为任意可能的值的情况,用一条 LEA 指令可以计算 a 的哪些倍数?

可计算出 $2^k$ (当 b 为 0)和 $2^k+1$ (当 了为 a)的值。

a 的倍数 LEA 指令
2a (a<<1)+0
3a (a<<1)+a
4a (a<<2)+0
5a (a<<2)+a
8a (a<<3)+0
9a (a<<3)+a

对于常数 K 的表达式 x * K 的生成代码,编译器会将 K 的二进制表示表达为一组 0 和 1 交替的序列:

例如,14 可写成 $[(0\cdots0)(111)(0)]$。考虑一组从位位置 $n$ 到位位置 $m$ 的连续的 1($n \geq m$)(对于 14 来说,有 $n=3,m=1$)。可以用下面两种不同形式中的一种来计算这些位对乘积的影响:

形式 A:(x<<n)+(x<<(n-1))+...+(x<<m)

形式 B:(x<<(n+1))-(x<<m)

这种优化只在需少量移位、加减法时才进行。

练习题 2.39 p107

对于位位置 n 为最高有效位的情况,要怎样修改形式 B 的表达式?

此时:x(<<n+1)) 溢出为 0,故结果为 -(x<<m)

练习题 2.40 p107

基于形式 A 和 B,填写 x*K 的优化表达式。

K 移位 加法/减法 表达式
6 2 1 (x<<2) + (x<<1) 即 4+2
31 1 1 (x<<5)-x 即 32-1
-6 2 1 (x<<1)-(x<<3) 即 2-8
55 2 2 (x<<6)-x(<<3)-x 即 64-8-1

练习题 2.41 p107

对于形式 A 和 B,编译器如何决定使用哪种?

选择操作次数少的形式。

首先当 $m > 0$ 时:当 $n=m$ 时,形式 A 只需 1 个移位,而形式 B 需 2 移位 1 减法,选 A;当 $n=m+1$ 时,A 和 B 都需要 2 移位 1 加减;当 $n>m+1$ 时,A 需要 $n-m+1>2$ 个移位和 $n-m>1$ 个加法,B 还是 2 移位 1 减法。对于 $m=0$ 时,形式 A 和 B 都会少 1 移位,还适用同样的选择原则。

除以 2 的幂

可以用移位来实现,无符号的用逻辑右移,补码的用算术右移。

规定整数除法总是舍入到 0,即舍入到最接近 0 的整数,即当实际值为正数 3.14 时,将向下舍入到 $\lfloor 3.14 \rfloor = 3$,而若实际值为负数 -3.14 时,将向上舍入到 $\lceil -3.14 \rceil=-3$。

无符号数除以 2 的幂,只需逻辑右移

k >>k(二进制) 十进制 $12340/2^k$
0 0011000000110100 12340 12340.0
1 0001100000011010 6170 6170.0
4 0000001100000011 711 771.25
8 0000000000110000 48 48.203125

上面是整数 12340 进行逻辑右移的值,可见无符号数除以 2 的幂,只需进行 $k$ 位的逻辑右移,其效果和除以 $2^k$ 再舍入到 0 是一样的。

补码数除以 2 的幂

补码 x 进行算术右移的结果总是向下舍入的。当 x 为非负数时,和上面的无符号数进行逻辑右移一样,舍入到 0(向下舍入)。但当 x 为负数时,以 -12340 为例:

k >>k(二进制) 十进制 $-12340/2^k$
0 1100111111001100 -12340 -12340.0
1 1110011111100110 -6170 -6170.0
4 1111110011111100 -712 -771.25
8 1111111111001111 -49 -48.203125

可见,当负数时,算术右移的结果总是向下舍入,与规定的舍入到 0 不符。

此时,可利用编置技术,在执行算术右移前,先加上适当的偏置量,再进行算术右移,从而使结果满足舍入到 0 的规定。

k 偏量 -12340+偏量 >>k(二进制) 十进制 $-12340/2^k$
0 0 1100111111001100 1100111111001100 -12340 -12340.0
1 1 1100111111001101 1110011111100110 -6170 -6170.0
4 15 1100111111011011 1111110011111101 -711 -771.25
8 255 1101000011001011 1111111111010000 -48 -48.203125

偏置技术利用了如下属性:对于整数 $x$ 和 $y(y>0)$,$\lceil x/y \rceil = \lfloor (x+y-1)/y \rfloor$。

当 $y=2^k$ 时,$x+y-1=x+2^k-1=x+(1«k)-1$,故补码 $x$ 的算术右移的表达式为 (x<0 ? x+(1<<k)-1 : x) >> k

练习题 2.42 p111

写一个函数 div16,对于整数参数 x 返回 x/16 的值。要求不能使用除法、模运算、乘法、条件语句 (if 和 ?:)、比较运算符、循环等。假设 int 为 32 位,补码,算术右移。

int div16(int x)
{
    /* (x + bias) >> k:
    对于 x 为正数时, bias 为 0
    对于 x 为负数时, bias = (1<<4)-1=15
    */
    int bias = (x>>31) & 0xF;
    return (x+bias) >> 4;
}

练习题 2.43 p111

下面的代码中,省略了常数 M 和 N 的定义:

#define M    /* Mystery number 1 */
#define N    /* Mystery number 2 */
int arith(int x, int y)
{
    int result = 0;
    result = x*M + y/N;
    return result;
}

用某个 M 和 N 的值编译上面代码后,编译器将优化乘除操作。下面是产生的机器码翻译回 C 的结果:

/* Translation of assembly code for arith */
int optarith(int x, int y)
{
    int t = x;
    x <<= 5;  
    x -= t;  /*  x = x*32 -x,使用了形式 B的优化:`(x<<(n+1))-(x<<m)`,
    这里 n=4, m=0, 从而 M = [1111] = 15 */
    if (y < 0) y += 7; /* y + (1<<3)-1 */
    y >>= 3; /* Arithmetic shift,从而 N  2  3 次方,即为 8
    return x+y;
}

M = 15, N=8

关于整数运算的最后思考

整数表示的有限字长限制了运算结果的取值范围,从而可能会溢出。无论是补码表示还是无符号数,它们的加减乘除操作,在位级上都完全一样或非常类似。

C 中的 unsigned 数据类型变量,在运算中会隐式地要求将其它参数先转换成 unsigned 数据类型再计算,从而会导致难以察觉的 BUG。

练习题 2.44 p112

假设有符号值使用补码、32 位、算术右移,无符号逻辑右移。变量的声明和初始化如下:

int x = foo(); /* Arbitrary value */
int y = bar(); /* Arbitrary value */

unsigned ux = x;
unsigned uy = y;

对于下面各 C 表达式, 1、证明对于所有的 x 和 y值,都为真,或者 2、给出使它不为真时的 x 和 y 值。

A. (x>0) || (x-1<0): x 为 0 时为真,为正数时为真,为 TMin = $-2^{31}` 时,$x-1=-2^{31}-1=2^{31}-1=TMax > 0$,此时不成立。

B. (x&7) != 7 || (x<<29 < 0): 左边要求最低 3 位有效位不能同时为 1,右边的要求最低的第 3 位为 1, 故例如当最低有效位为 011 时不成立。

C. (x*x) > =0:当 $x=2^{16}-1$ 时,$x*x = 2^{32} + 1 - 2^{17} = -2^{17} -1$,故不成立。

D. x<0 || -x <=0: 根据定义,

,故都成立。

E. x>0 || -x >=0: x=Tmin 时, -x=Tmin,此时不成立。

F. x+y == uy+ux:由于位级操作相同,只是解析不同,故都成立。

G. x*~y + uy*ux == -x: 因 -y = ~y+1,即 ~y=-y-1,同时,乘法的位级操作也相同,故 uyuy=yx,故 x~+uyux = x(-y-1)-yx = -x,故都成立。

参考

]]>
CSAPP3 2.2 整型数据类型 2017-07-21T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/CSAPP3-2.2_integer_representations 整数的数据与算术操作术语如下:

符号 类型 含义
$B2T_w$ 函数 二进制转补码
$B2U_w$ 函数 二进制转无符号数
$U2B_w$ 函数 无符号数转二进制
$U2T_w$ 函数 无符号数转补码
$T2B_w$ 函数 补码转二进制
$T2U_w$ 函数 补码转无符号数
$TMin_w$ 常数 最小补码值
$TMax_w$ 常数 最大补码值
$UMax_w$ 常数 最大无符号数
$+^t_w$ 操作 补码加法
$+^u_w$ 操作 无符号数加法
$*^t_w$ 操作 补码乘法
$*^u_w$ 操作 | 无符号数乘法  
$-^t_w$ 操作 补码取反
$-^u_w$ 操作 | 无符号数取反  

整型数据类型

C 中的有符号整型的取值范围是不对称的,负数的范围比正数的范围大 1,如 char 的为 [-128, 127]。

但是 C 标准中定义的每种有符号整型的取值范围是对称的,如 char 的为 [-127,127]。

无符号整数的编码

假设有一种整型有 $w$ 位。我们将位向量写成 $\vec x$,表示整个向量,或者写成 $[x_{w-1},x_{w-2},\cdots,x_0]$,表示向量中的每一位。把 $\vec x$ 看成一个二进制表示的数,就获得了 该向量的无符号表示。

在这种编码中,每个位 $x_i$ 都取值 0 或 1,后一种取值意味着数值 $2^i$ 应为数字值的一部分。用一个函数 $B2U_w$ 来表示:

原理:无符号数编码的定义

对向量 $\vec x = [x_{w-1},x_{w-2},\cdots,x_0]$:

函数 $B2U_w$ 将一个长度为 $w$ 位的 0、1 串映射到非负整数。例如:

$B2U_4([0101]) = 0\bullet2^3 + 1\bullet2^2 + 0\bullet2^1 + 1\bullet2^0 = 5$

当用长度为 $2^i$ 的指向右侧箭头的条表示每个位的位置 $i$ 时,位向量对应的数值就等于所有值为 1 的位对应的条的长度之和。

无符号数数值计算

$w$ 位无符号数,最小值用向量 $[00\cdots0]$ 表示,即整数值 0,最大值用向量 $[11\cdots1]$ 表示,即整数值 $UMax_w := \sum_{i=0}^{w-1} = 2^w-1$。即取值区间是 $[0, 2^w-1]$。

函数 B2U 是一个双射,从而保证了无符号数编码的唯一性。

有符号整数编码

通常用补码 (two’s-complement) 形式编码。在这种定义中,将字的最高有效位解释为负权(negative weight)。

原理:有符号数编码的定义

对向量 $\vec x = [x_{w-1},x_{w-2},\cdots,x_0]$:

最高有效位 $x_{w-1}$ 称为符号位,它的权重为 $-2^{w-1}$。符号位设置为 1 时,表示值为负,为 0 时表示非负。

函数 $B2T_w$ 将一个长度为 $w$ 位的 0、1 串映射到有符号整数。例如:

$B2T_4([0101]) = -0\bullet2^3 + 1\bullet2^2 + 0\bullet2^1 + 1\bullet2^0 = 5$

$B2T_4([1101]) = -\bullet2^3 + 1\bullet2^2 + 0\bullet2^1 + 1\bullet2^0 = -3$

当用向左指的条表示符号位的负权重,则位向量对应的数值就等于可能的向左指的条和向右指的条架起来决定。

有符号数数值计算

$w$ 位有符号数,最小值用向量 $[10\cdots0]$ 表示,即整数值 $TMin_w := -2^{w-1}$,最大值用向量 $[01\cdots1]$ 表示,即整数值 $TMax_w := \sum_{i=0}^{w-2} = 2^{w-1}-1$。即取值区间是 $[-2^{w-1}, 2^{w-1}-1$。

函数 B2T 是一个双射,从而保证了有符号数编码的唯一性。

练习题 2.17 p82

补全下表:

$\vec x$ hex $\vec x$ bin $B2U_4(\vec x)$ $B2T_4(\vec x)$
0xE [1110] 8+4+2=14 -8+4+2=-2
0x0 [0000] 0 0
0x5 [0101] 4+1=5 4+1=5
0x8 [1000] 8 -8
0xD [1101] 8+4+1=13 -8+4+1=-3
0xF [1111] 8+4+2+1=15 -8+4+2+1=-1
上面可见,补码的范围是不对称的, $ TMin = TMax +1$,即 TMin 没有与之对应的正数。只所以不对称是因为一半的位模式(符号位为1)表示负数,另一半(符号位为0)表示非负(0 和正数)。

最大的无符号数刚好比补码的最大值的 2 倍大 1:UMax = 2TMax + 1。-1 和 UMax 有同样的位表示(全 1 的串)。而 0 在两种表示中都是全 0 的串。

C 标准没有要求用补码来表示有符号整数,但是几乎所有机器都是这么做的。<limits.h> 中定义了一组整型常量,如 INT_MAX, INT_MIN, UNIT_MAX 等,使用它们可提高可移植性。

固定长度的整型

C99 标准在 <stdint.h> 中引入了一组固定长度的整型,声明形如 intN_t 和 unitN_t,N 通常的值可为 8、16、32、64。可用来无歧义地定义整型。

这些整型同时对应有一组宏,用来定义每个 N 对应的整型的最小和最大值,如 INT32_MIN, INT32_MAX, UINT32_MAX 等。

对这些整型使用 printf 格式输出也需要宏,从而系统能将它扩展成正确的格式中。例如,变量 x 和 y 的类型是 int32_t 和 uint64_t,它们的 printf 输出代码应为 printf("x=%" PRId32, ",y=%" PRIu64 "\n", x, y);

编译为 64 位程序时,宏 PRId32 展开为 “d”,宏 PRIu64 展开为 “lu”。当 C 预处理器遇到仅用空格(或其它空白符)分隔的字符串常量序列时,会将它们串联起来作为一个字符串。

Java 标准对整型定义是很明确的,要求整数都是有符号数,用补码表示。

有符号数的其它表示方法

原码(Sign-Magnitude): 最高有效位是符号位,用来确定剩下的位应该取负权还是正权:

反码(Ones’ Complement): 除了最高有效位的权是 $-(2^{w-1}-1)$ 而不是 $-2^{w-1}$,它和补码一样。

这种表示方法中,对数字 0 都有两种不同的编码方式。它们把 $[00\cdots0]$ 都解释为 +0,而 -0 在原码中表示为 $[10\cdots0]$,在反码中为 $[11\cdots1]$。

原码编码在浮点数中有应用。

注意补码(Two’s complement) 和反码(Ones’ complement) 中的撇号位置不同。术语补码来源于:对于非负数 $x$,我们用 $2^w-x$ (这里只有一个 2)来计算 $-x$ 的 $w$ 位表示。而术语反码:用 $[11\cdots1]-x$ (这里有多个 1) 来计算 $-x$ 的反码表示。

补码表示时,绝对值相等的正数和负数的位模式

对于 32 位的有符号数 x = 12345 = 0x3039,mx = -x = -12345 = 0xcfc7

3 0 3 9

0011 0000 0011 1001

c f c 7

1100 1111 1100 0111

要根据正数的位模式,计算出其对应负数的位模式,可以将正数的位模式,取反加 1 得到。

例如正数 12345 = 0x3039, 取反加 1 后为 0xcfc7,刚好为 -12345。

练习题 2.18 p84

反汇编器生成的程序码都使用补码形式表示数值。将下面的 32 位补码表示的十六进制值转换为等值的十进制值。

A. 0x2e0 = 2256+1416= 736

B. -0x58 = -(5*16 + 8) = -88

C. 0x28 = 2*16 +8 = 40

D. -0x30 = -48

E. 0x78 = 7*16 + 8 = 120

F. 0x88 = 8*16 + 8 = 136

G. 0x1f8 = 1256 + 1516 + 8 = 504

H. 0xc0 = 12*16 = 192

I. -0x48 = -(4*16 + 8) = -72

有符号数和无符号数的转换

C 中强制类型转换的结果保持位表示不变,只改变解释这些位的方式。

如:

short int v = -12345; /* 0xcfc7 */
unsigned short uv = (unsigned short) v;  /* 0xcfc7 */
printf("v = %d, uv= %u\d", v, uv); /* v= -12345, uv=53191 */

更一般地,可用数学化表示如下: 给定 $0 \leq x \leq UMax_w$ 中的任一整数 $x$,函数 $T2B_w(x)$ 给出 $x$ 的唯一 $w$ 位无符号表示。相似地,当 $x$ 满足 $TMin_w \leq x \leq TMax_w$ 时,函数 $T2B_w(x)$ 会给出 $x$ 的唯一 $w$ 位补码表示。

则 $T2U_w(x) := B2U_w(T2B_w(x))$ 将补码表示的有符号数转为无符号数,输入区间为 $[TMin_w, TMax_w]$,输出区间为 $[0, UMax_w]$,输入和输出间有相同的位模式。类似地,$U2T_w(x) := B2T_w(U2B_w(x))$ 将无符号数转为有符号数。

无符号表示中的 $UMax$ 有着和补码表示的 -1 相同的位模式,同时 $1+UMax_w = 2^w$。

练习题 2.19 p87

利用 练习题 2.17 时填写的表格,补全下面 $T2U_4$ 的表格。

$x$ $T2U_4(x)$
-8 8
-3 13
-2 14
-1 15
0 0
5 5

可见,在对于负数,$T2U_w(x)$ 的值是 $2^w + x$。

当补码转换为无符号数时,

因此,$B2U_w(\vec x) = x_{w-1}2^w + B2T_w(\vec x)$, $T2U_w(x) = B2U_w(T2B_w(x)) = x + x_{w-1}2^w$。

故对于 $TMin_w \leq x \leq TMax_w 的 $x$ 有:

补码到无符号数

练习题 2.20

解答 2.19 题时,$w = 4$,故当负数时的转换只需加上 $2^4 = 16$ 即可。

类似地,对于无符号数转换为补码有:

对满足 $0 \leq u \leq UMax_w$ 的 $u$ 有:

$$U2T_w(u) = \begin{cases} u, u \leq TMax_w
u - 2^w, u > TMax_w
\end{cases}

无符号数到补码

总结下,对于 $[0, TMax_w]$ 内的 $x$,数字的无符号和补码表示都相同。而在这个范围外的数值,转换时需要加上或者减去 $2^w$。

C 中的有符号数和无符号数

C 实现中一般都用补码表示有符号数,并且常数等大多数数字默认都是有符号的,要创建一个无符号常量,要加后缀 ‘u’ 或 ‘U’,如 12345U,0x1A2Bu。

当执行一个运算时,如果它的一个运算数是有符号的,而另一个是无符号的,那么 C 会隐式地将有符号参数强制类型转换成无符号数(即升级规则 promotion rule),并假设这两个数是非负的,来运行这个运算。这种方法对算术运算影响不大,并对大小比较运算影响较大,会引起 Bug。例如,对于 -1 < 0U,由于 0U 是无符号的,-1 将隐式地转换为无符号数 $UMax_w$,从而使得运算表达式为假。

练习题 2.21 p89

假设在采用补码运算的 32 位机器上,补全下表:

表达式 类型
-2147483647-1 == 2147483648U 无符号数 左边 TMin 转为 UMax,故相等 1
-2147483647-1 < 2147483647 有符号数 | 左边 TMin 右边 TMax,1  
-2147483647-1U < 2147483647 无符号数 左边转为无符号数 $2^{31}$,大于右, 0
-2147483647-1 < -2147483647 有符号数 左边 TMin, 右边为 TMin+1,故 1
-2147483647-1U < -2147483647 无符号数 左边转为无符号数 $2^{31}$,右转为$2^{31}+1$,故 1

扩展数字的位表示

  1. 无符号数进行 0 扩展,即左边补上多个 0
  2. 补码数字进行符号扩展(用归纳法证明)

练习题 2.22 p91 因此,下面的 3 个位模式都是 -5 的补码表示。 A. [1011], B. [11011], C. [111011]。

无符号数和有符号数间转换的相对顺序能够影响程序的行为,如:

short sx = -12345; /* cfc7 */
unsigned uy = sx; /* 相当于 (unsigned) (int) sx */

printf("uy=%u:\t", uy);
show_bytes((byte_pointer) &uy, sizeof(unsigned));

上面在大端法机器上输出: uy=4294954951: ff ff cf c7

这表示当把 short 转为 unsigned 时,先改变大小,变成 int,再改变类型,成 unsigned。

练习题 2.23 p92

考虑下面 2 个 C 函数:

int fun1(unsigned word)
{
    return (int) ((word << 24) >> 24);
}

int fun2(unsigned word)
{
    return ((int) word << 24) >> 24);
}

假设在采用补码的 32 位机上运行,且有符号数是算术右移,无符号数是逻辑右移。填写下表。

w fun1(w) fun2(w)
0x00000076 (int)0x00000076=118 0x00000076=118
0x87654321 (int)0x00000021=33 0x00000021=33
0x000000C9 (int)0x000000C9=201 0xFFFFFFC9=-54
0xEDCBA987 (int)0x00000087=135 0xFFFFFF87=-120

fun1 中是在无符号数上右移地,是逻辑右移,fun2 中 word 先被转换成了有符号数 int,故是算术右移。

B. 函数 fun1 完成提取 word 的低 8 位的功能。fun2 完成提取后,还进行符号扩展。

数字截断

  1. 无符号数的截断直接截去高位多余的位,例如截断到 k 位,相当于进行了 mod $2^k$ 操作。
  2. 有符号数的截断也是直接截去高位多余的位,但是数值要进行补码数值的解析。

练习题 2.24 p93

假设将一个 4 位的数(0~F) 截断到 3 位数据(0~7)。填写下表。

原始值 hex 截断值 hex 原始值(无符号) 截断值(无符号) 原始值(补码) 截断值(补码)
0(0000) 0(000) 0(0000) 0(000) 0(0000) 0(000)
2(0010) 2(010) 2(0010) 2(010) 2(0010) 2(010)
9(1001) 1(001) 9(1001) 1(001) -7(1001) 1(001)
B(1011) 3(011) 11(1011) 3(011) -5(1011) 3(011)
F(1111) 7(111) 15(1111) 7(111) -1(1111) -1(111)

有关有符号数与无符号数的建议

当表达式中含有无符号数时,有符号数将自动隐式转换成无符号数,这将可能导致漏洞。避免这类错误的一种依法是绝不使用无符号数,实际上,除了 C 外很少有语言支持无符号整数。

只有当想把字仅仅看做是位的集合而没有任何数字意义时,无符号数才非常有用。

练习题 2.25 p94

下列代码试图计算数组 a 中所有元的各,当参数 length 等于 0 时,运行这段代码应该返回 0.0,但实际上会遇到内存错误。为什么?改进?

/* WARNING: This is buggy code */
float sum_elements(float a[], unsigned length)
{
    int i;
    float result = 0;
    
    for (i=0; i <= length -1; i++)
        result += a[i];
    return result;
}

由于 length 是无符号数, 故 length -1 在当 length 为 0 时将不是 -1 ,而是 UMax,故会遇到内存出错。

改进:将 length 声明改为 int 即可。或者判断改为 i<length

练习题 2.26 p94

要写一个函数用来判定一个字符串是否比另一个更长。前提是要用字符串库函数 strlen,它的声明为: size_t strlen(const char *s);,最开始你写的函数是这样的:

/* Determine whether string s is longer than string t */
/* WARNING: This function is buggy */
int strlonger(char *s, char *t) {
    return strlen(s) - strlen(t) > 0;
}

当进行一些测试时,似乎一切正确。进一步研究发现在头文件 stdio.h 中数据类型 size_t 是定义成 unsigned int 的。

A. 在当 s 的长度小于 t 的长度时,结果会不正确。

B. 这是因为,在 A 情况下,负数会隐式转成了正数。

C. 修改: 改为 return strlen(s) > strlen(t);

参考

]]>
CSAPP3 2.1 信息存储 2017-07-20T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/CSAPP3-2.1_info_storage 无符号整数 (unsigned) 基于传统的二进制表示法编码,表示大小或等于 0 的数字。有符号整数通常用补码 (two’s-complement) 编码。浮点数 (floating-point) 编码表示实数以 2 为基数的科学记数法的版本,因此是一种近似表示。

计算机用有限位来编码数字,因此,当去处结果太大时会出现溢出 (overflow)等。

十六进制表示法

一个字节(8位)由两个十六进制数表示。在进行二进制、十进制、十六进制间的转换时,只需记住 A、C、F的十进制值和对应的二进制表示,其它的都可对应快速计算出。 (A, 1010, 10), (C, 1100, 12), (F, 1111, 15)。

练习题2.1 p62

A. 将 0x39A7F8 转换为二进制。

11 1001 1010 0111 1111 1000

3 9 A 7 F 8

B. 将二进制 1100100101111011 转换为十六进制。

1100 1001 0111 1011

C 9 7 B

C. 将 0xD5E4C 转换为二进制。

1101 0101 1110 0100 1100

D 5 E 4 C

D. 将二进制 1001101110011110110101 转换为十六进制。

10 0110 1110 0111 1011 0101

2 6 E 7 B 5

当 $x$ 是 2 的非负整数 $n$ 次幂时,即 $x=2^n$ 时,$x$ 的二进制形式即为 1 后面加 n 个 0,而十六进制数字 0 表示 4 个二进制 0,因此十六进制表示时为 1 后面加 n/4 个 0。所以,当 $n$ 表示成 $i+4j$ 形式时(其中 $0 \leq i \leq 3$),可以把 $x$ 写成开头的十六进制数字为$1(i=0)$, $2(i=1)$, $4(i=2)$, $8(i=3)$,后面跟 $j$ 个 0。比如 $x=2048=2^{11}$ 时,有 $n=11=3+4*2$,从而十六进制表示为 $0x800$

练习题 2.2 p62

给出 2 的不同次幂的二进制和十六进制表示:

$n$ $2^n$(十进制) $2^n$(十六进制)
9, $1+4\bullet2$ 512 0x200
19, $3+4\bullet4$ $1024\bullet512$=524288 0x80000
14, $2+4\bullet3$ 16384=$1024\bullet16$ 0x4000
16, $0+4\bullet4$ $1024\bullet64$=65536 0x10000
17, $1+4\bullet4$ $1024\bullet128$=131072 0x20000
5,$1+4\bullet1$ 32 0x20
7,$3+4\bullet1$ 128 0x80

十六进制数字和十进制间的相互转换通过除以 16 和乘以 16 的幂进行。

练习题2.3 p62 补全下面的二进制、二进制、十六进制对应表。

十进制 二进制 十六进制
0 0000 0000 0x00
$167=10\bullet16+7$ 1010 0111 0xA7
$62=3\bullet16+14$ 0011 1110 0x3E
$188=11\bullet16+12$ 1011 1100 0xBC
32+16+7,55 0011 0111 0x37
128+8,136 1000 1000 0x88
128+64+32+16+3,243 1111 0011 0xF3
128+16+2,146 1001 0010 0x52
128+32+8+4,172 1010 1100 0xAC
128+64+32+7,131 1110 0111 0xE7

十进制和十六进制间的转换,也可以直接通过搜索引擎获取。

练习题2.4 p63

不通过转换,直接进行十六进制加减。

A. 0x503c + 0x8 = 0x5044

B. 0x503c - 0x40 = 0x4FFC

C. 0x503c + 64 = 0x503c + 0x40 = 0x507c

D. 0x50ea = 0x503c = 0xAE

字数据大小

虚拟地址用一个字来编码,因此字长决定了程序可访问的最大虚拟地址空间大小。

寻址和字节顺序

在几乎所有的机器上,多字节对象都是被存储为连续的字节序列,并且用字节中最小的地址作为对象的地址。例如,对于 4 字节的 int 变量 x 的地址为 0x100,则 x 的 4 个字节被存储在 0x100, 0x101, 0x102, 0x103 位置。

排序表示一个对象的字节有 2 种通用规则。例如有 $w$ 位的整数,其位表示为 $[x_{w-1},x_{w-2},\cdots,x_1,x_0]$,其中 $x_{w-1}$ 是最高有效位,而 $x_0$ 是最低有效位。假设 $w$ 是 8 的倍数,这些位就能被分组成字节,其中最高有效字节包含位$[x_{w-1},x_{w-2},\cdots,x_{w-8}]$,而最低有效字节包含位$[x_7,x_6,\cdots,x_0]$。

有些机器选择从最低有效字节到最高有效字节的顺序存储对象,即最低有效字节保存在最低地址位置,这种叫小端法 (little endian);而最高有效字节存储在最前面的方式,叫大端法(big endian)。

假设 4 字节的 int 型变量 x 位于地址 0x100处,其值为 0x01234567,则其地址范围为 0x100~0x103。

那么大端法中, 从地址 0x100 到 0x103 存储的字节值依次为 0x01, 0x23, 0x45, 0x67;而小端法依次存储的是 0x67, 0x45, 0x23, 0x01

大多数 Intel 兼容机使用小端法,此时在看其字节级指令时,要注意其中给出的数值字节序。书写字节序列的自然方式是最低位字节在左边,而最高位字节在右边,这正好和通常书写数字时最高有效位在左边,最低有效位在右边相反。因此,当小端法机器的字节指令中的数值序列为 78 56 34 12 时,表示的值将是 0x12345678。

用下面的 show_types 来输出对象的字节内容:

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len){
    size_t i;
    for (i=0; i<len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}

void show_int(int x) {
    show_bytes((byte_pointer) &x, sizeof(int));
}

void show_float(float x) {
    show_bytes((byte_pointer) &x, sizeof(float));
}

void show_int(void *x) {
    show_bytes((byte_pointer) &x, sizeof(void *));
}

练习题 2.5 p69

思考下面对 show_bytes 的三次调用:

int val = 0x87654321; byte_pointer valp = (byte_pointer) &val; show_bytes(valp, 1); /* A. */ show_bytes(valp, 2); /* B. */ show_bytes(valp, 3); /* C. */

指出在小端法和大端法机器上,每次调用的输出值。

在小端法机器上,变量从低地址开始存储的字节值为 21 43 65 87,而大端法的为 87 65 43 21。 故:

A. 小端法: 21, 大端法:87

B. 小端法: 21 43, 大端法:87 65

C. 小端法: 21 43 65, 大端法:87 65 43

练习题 2.6 p70

使用 show_int 和 show_float, 我们确定整数 3510593 的十六进制为 0x00359141,而浮点数 3510593.0 的十六进制表示为 0x4A564504。

A:写出这两个十六进制值的二进制表示。

0000 0000 0011 0101 1001 0001 0100 0001

0 0 3 5 9 1 4 1

0100 1010 0101 0110 0100 0101 0000 0100

4 A 5 6 4 5 0 4

B: 移动这两个二进制串的相对位置,使得它们相匹配位数最多,有多少位匹配?

00000000001 101011001000101000001

010010100 101011001000101000001 00

一共有 21 位匹配。

C:串中的什么部分不相匹配?

整数中除了最高有效位 1 ,其它位都嵌在浮点数中,浮点数中有一些非零的高位不与整数中的高位匹配。

练习题 2.7 p70

下面对 show_bytes 的调用将输出什么?

const char *s = "abcdef";
show_bytes((byte_pointer)s, strlen(s));

注意字母 ‘a’ ~ ‘z’ 的 ASCII 码为 0x61 ~ 0x7A。

61 62 63 64 65 66

布尔代数

0 和 1 之间的与、或、非、异或操作。位向量的操作。

练习题 2.8 p72

给出位向量的布尔运算的求值结果。

运算 结果
a [01101001]
b [01010101]
~a [10010110]
~b [10101010]
a&b [01000001]
a|b [01111101]
a^b [00111100]
位向量可用来表示有限集合。可以用位向量 $[a_{w-1},\cdots,a_1,a_0]$ 编码任何子集 $A \subseteq {0, 1, \cdots, w-1}$,其中 $a_i=1$ 当且仅当 $i \in A$。这里我们将 $a_{w-1}$ 写在左边,$a_0$写在右边,则位向量a=[01101001] 表示集合 A = {0, 3, 5, 6}, 而 b=[01010101] 表示集合 B = {0, 2, 4, 6}。此时,位向量的 和 & 运算分别对应于集合的并和交,而 ~ 对应于集合的补。实际中,位向量可用来表示中断信号掩码。

练习题 2.9 p73

位向量的与、或、非、异或操作。

RGB 颜色的位向量为 [RGB]

A. 位向量中的每一位取反。

B. 布尔运算结果如下:

Blue Green = [001] [010] = [011] = Cyan

Yellow & Cyan = [110] & [011] = [010] = Green

Red ^ Magenta = [100] ^ [101] = [001] = Blue

C 语言中也支持位级的布尔运算

进行运算时,先将数值从十六进制转成二进制,运算后再转换回十六进制。

练习题 2.10 p74

利用位向量的 a^a=0 的属性,可用下面的程序进行两变量的值交换,而无需使用到一个临时存储变量。

void inplace_swap(int *x, int *y)
{
    *y = *x ^ *y; /* Step 1 */
    *x = *x ^ *y; /* Step 2 */
    *y = *x ^ *y; /* Step 3 */
 }

补全每步运算后的 x 和 y 值。

程序依赖两个事实,^ 操作是可交换和可结合的,以及任意的向量 a^a=0 的属性。

步骤 *x *y
初始 a b
Step 1 a a^b
Step 2 a^(a^b)=b a^b
Step 3 b b^(a^b)=a

上面的推理隐含地假设了 x 和 y 代表不同的位置。但当 x 和 y 值指向相同的位置时,会将值设置为了 0,如题 2.11 如示。

练习题 2.11 p74

在 2.10 的 inplace_swap 函数的基础上,你写出下面的函数来将一个数组中的元素头尾两端依次对调。

void reverse_array(int a[], int cnt)
{
    int first, last;
    for (first=0, last=cnt-1;
        first <= last;
        first++, last--)
        inplace_swap(&a[first], &a[last]);
}

该代码当数组是偶数长度时是正确的,如输入 1 2 3 4, 输出为 4 3 2 1,但对奇数长度时会出错,如输入 1 2 3 4 5,输出 5 4 0 2 1,会把中间的值设置为了 0。

A. 对于长度为奇数的数据,长度 $cnt = 2k + 1$,函数最后一次循环时,变量 first 和 last 的值都为 k。

B. 这时调用 inplace_swap 时,由于 a^a = 0,所以将元素设置为了 0。

C. 对 reverse_array 代码做哪些简单改动能消除这个问题。

只需将 first <= last 调整为 first < last 即可。

位级运算可用于掩码计算,发 ~0 将生成一个全 1 的掩码。

练习题 2.12 p75

对于下面的值,写出变量 x 的 C 语言表达式。代码要对任何字长 $w \geq 8$ 都能工作。我们给出了当 x=0x87654321 及 $w=32$ 时表达式求值的结果。

A. x 的最低有效字节,其它位均置为 0。[0x00000021]。 x & 0xFF

B. 除了 x 的最低有效字节外,其它的位都取补,最低有效字节保持不变。 [0x789ABC21]

x^ (~0xFF)

C. x 的最低有效字节设置成全 1,其它字节都保持不变。 [0x876543FF]

x 0xFF

练习题 2.13 p75

bis(位设置)函数和 bic(位清除)函数都输入一个数字字 x 和一个掩码字 m,并返回一个结果 z。bis 将在 m 为 1 的每个位置上将 x 对应的位设置为 1,而 bic 将 m 为 1 的每个位置上将 x 对应的位设置为 0。只使用 bic, bis 实现按位 和 ^ 运算。提示:写出 bis, bic 运算的 C 表达式。
/* Declarations of funs implementing operations bis and bic */
int bis(int x, int m);
int bic(int x, int m);

/* bis(x,y): 返回 x|y
 bic(x,y): 返回 x & (~y) */
 
/* Compute x|y using only calls to funs bis and bic */
int bool_or(int x, int y)
{
    int result = bis(x,y); /* bis 即为 | 操作 */
    return result;
}

int bool_xor(int x, int y)
{
    /*
    异或操作:相同为 0,不同为 1:
    xor: bic(x,y) | bic(y,x)
    */
    int result = bis( bic(x,y), bic(y,x));
    return result;
}

C 语言中的逻辑运算

逻辑运算将非零认为 TRUE(值为 1),FALSE 值为 0,并且运算时,当前面的参数求值能确定表达式结果时,就立即返回,不对后面的进行求值。

练习题 2.14 p76

假设 x=0x66, y=0x39,填写下表各值:

x = 0110 0110

~x= 1001 1001

y = 0011 1001

~y= 1100 0110

表达式 表达式
x & y 0010 0000=0x20 x && y 1
x | y 0111 1111=0x7F x || y 1
~x | ~y 1101 1111=0xDF !x || !y 0
x & !y 0 x && ~y 1

**练习题 2.15 p76 **

只使用位级和逻辑运算,编写一个 C 表达式,它等价于 x==y。即当 x 和 y 相等时返回 1,否则返回 0。

相等时,异或操作将为 0,刚表达式为 !(x^y)

C 语言中的移位运算

逻辑右移在左端补 0,算术右移在左端补最高有效位的值,以保持有符号整数的符号值。C 标准没有规定使用哪种右移,但是几乎所有的编译器/机器都对有符号数使用算术右移。对于无符号数,右移必须是逻辑的。

而 Java 有明确定义,x>>k 为算术右移,而 x>>>k 为逻辑右移。

当位移量 k 大于数据类型的位数时,C 标准没有定义如何做,但一般是移位 $k mod w$ 位,而 Java 中是明确移位 $k mod w$ 位的。

练习题 2.16 p77

移位运算时最好使用二进制表示。填写下表值:

x hex x bin x«3 bin x«3 hex x»2(逻辑) bin x»2(逻辑) hex x»2(算术)bin x»2(算术) hex
0xC3 1100 0011 0001 1000 0x18 0011 0000 0x30 1111 0000 0xF0
0x75 0111 0101 1010 1000 0xA8 0001 1101 0x1D 0001 1101 0x1D
0x87 1000 0111 0011 1000 0x38 0010 0001 0x21 1110 0001 0xE1
0x66 0110 0110 0011 0000 0x30 0001 1001 0x19 0001 1001 0x19

参考

]]>
CSAPP3 1 计算机系统漫游 2017-07-20T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/CSAPP3-1-cs-tour 信息就是位 + 上下文

Amdahl 定律

当对系统的某部分加速时,某对整个系统的性能影响取决于该部分的重要性和加速程度。如果系统原来的运行时间为 $T_{old}$,提速部分运行时间所占比例为 $\alpha$,该部分的提速比例为 $k$,则该部分原来运行所需时间为 $\alpha T_{old}$,而提速后所需时间为 $(\alpha T_{old})/k$。因此,提速后的总运行时间为 由此,计算加速比为

因此,当 $\alpha=0.6$ (60%),加速因子$k=3$ 时, 加速比 $S=1/[0.4+0.6/3]=1.67$ 倍。可见,虽然对系统的一个主要部分做出了重大改进,但是整体系统的加速比还是很小。

Amdahl 定律的主要观点是:要想显著加速整个系统,必须提升全系统中相当大部分的速度。

当对某部分的加速因子 $k$ 走向于 $\infty$ 时,即运行时间可忽略不计时,整个系统的加速比为

此时,当 $\alpha=0.6$ 时,加速比 $=1/(1-0.6)=2.5$。可见,即使 60% 部分的系统做了最大改善,整体上也只能提速 2.5 倍。

参考

]]>
Python 2 标准库示例:4.3 calendar-处理日期值 2017-06-08T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/Python2Lib-dt-calendar 目的: 该模块实现了一些处理日期的类,用来管理年、月、周等值。

Python 版本: 1.4+, 并在 2.5 时有更新。

calendar 模块定义了 Calendar,封装了日历计算相关的方法。另外,子类 TextCalendarHTMLCalendar 可进行格式化输出。这些类默认将周一设置为每周的第一天(欧洲习惯),但可在初始化时通过传入参数修改(0 是 周一,…, 6 是周日,也可用模块中的常量指定,如 calendar.MONDAY)。

Calendar 实例的方法

import calendar

c = calendar.Calendar()

import calendar

c = calendar.Calendar()

# 按序输出一周中的每天的整数值,第一天值和 firstweekday 同
print '\niterweekdays():'
for d in c.iterweekdays(): 
    print d
    
# 返回某月 [1,12] 中的所有天 (datetime.date 对象), 为补全
#  整周,会包含之前月和之后月的某些天
print '\nitermonthdates(2017, 6):'
for d in c.itermonthdates(2017,6):
    print d
    
# 类似 itermonthdates(),但只返回整数表示的第几天,[0-31], 其中 0 表示为补全整周包含的
# 临近月中的天
print '\nitermonthdays(2017, 6):'
for d in c.itermonthdays(2017, 6):
    print d
    
# 类似 iermonhdays(),但返回中包含整数表示的第几天及周几的数据
print '\nitermonthdays2(2017, 6):'
for d in c.itermonthdays2(2017, 6):
    print d
    
# 日历数据都是按周组织的,
# 故返回周数据列表,
# 其中周数据是一个包含 7 个 datetime.date 对象的列表
print '\nmonthdatescalendar(2017, 6):'
for index, week in enumerate(c.monthdatescalendar(2017, 6)):
    print ' week', index+1, ':', week
    
# monthdayscalendar(), monthdays2calendar() 都和 monthdatescalenar()
# 类似,只不过前者返回的周数据中的元素是天数,而后者返回的周数据中的元素是 (天数,周几)

#yeardatescalendar(year[, width])
# 返回 width(默认 3) 个月为一组的组列表,组中的每个月数据和 monthdatescalendar()
# 返回的相同。
#而 yeardayscalendar(), yeardays2calendar() 也类似。
width = 3
print '\nyeardatescalendar(2017, ', width, '):'
for index, month_rows in enumerate(c.yeardatescalendar(2017, 3)):
    print ' row', index+1, ':'
    for index2, month in enumerate(month_rows):
        print '  month', index2+1, ':', month
iterweekdays():
0
1
2
3
4
5
6

itermonthdates(2017, 6):
2017-05-29
2017-05-30
2017-05-31
2017-06-01
2017-06-02
2017-06-03
2017-06-04
2017-06-05
2017-06-06
2017-06-07
2017-06-08
2017-06-09
2017-06-10
2017-06-11
2017-06-12
2017-06-13
2017-06-14
2017-06-15
2017-06-16
2017-06-17
2017-06-18
2017-06-19
2017-06-20
2017-06-21
2017-06-22
2017-06-23
2017-06-24
2017-06-25
2017-06-26
2017-06-27
2017-06-28
2017-06-29
2017-06-30
2017-07-01
2017-07-02

itermonthdays(2017, 6):
0
0
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
0
0

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

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

yeardatescalendar(2017,  3 ):
 row 1 :
  month 1 : [[datetime.date(2016, 12, 26), datetime.date(2016, 12, 27), datetime.date(2016, 12, 28), datetime.date(2016, 12, 29), datetime.date(2016, 12, 30), datetime.date(2016, 12, 31), datetime.date(2017, 1, 1)], [datetime.date(2017, 1, 2), datetime.date(2017, 1, 3), datetime.date(2017, 1, 4), datetime.date(2017, 1, 5), datetime.date(2017, 1, 6), datetime.date(2017, 1, 7), datetime.date(2017, 1, 8)], [datetime.date(2017, 1, 9), datetime.date(2017, 1, 10), datetime.date(2017, 1, 11), datetime.date(2017, 1, 12), datetime.date(2017, 1, 13), datetime.date(2017, 1, 14), datetime.date(2017, 1, 15)], [datetime.date(2017, 1, 16), datetime.date(2017, 1, 17), datetime.date(2017, 1, 18), datetime.date(2017, 1, 19), datetime.date(2017, 1, 20), datetime.date(2017, 1, 21), datetime.date(2017, 1, 22)], [datetime.date(2017, 1, 23), datetime.date(2017, 1, 24), datetime.date(2017, 1, 25), datetime.date(2017, 1, 26), datetime.date(2017, 1, 27), datetime.date(2017, 1, 28), datetime.date(2017, 1, 29)], [datetime.date(2017, 1, 30), datetime.date(2017, 1, 31), datetime.date(2017, 2, 1), datetime.date(2017, 2, 2), datetime.date(2017, 2, 3), datetime.date(2017, 2, 4), datetime.date(2017, 2, 5)]]
  month 2 : [[datetime.date(2017, 1, 30), datetime.date(2017, 1, 31), datetime.date(2017, 2, 1), datetime.date(2017, 2, 2), datetime.date(2017, 2, 3), datetime.date(2017, 2, 4), datetime.date(2017, 2, 5)], [datetime.date(2017, 2, 6), datetime.date(2017, 2, 7), datetime.date(2017, 2, 8), datetime.date(2017, 2, 9), datetime.date(2017, 2, 10), datetime.date(2017, 2, 11), datetime.date(2017, 2, 12)], [datetime.date(2017, 2, 13), datetime.date(2017, 2, 14), datetime.date(2017, 2, 15), datetime.date(2017, 2, 16), datetime.date(2017, 2, 17), datetime.date(2017, 2, 18), datetime.date(2017, 2, 19)], [datetime.date(2017, 2, 20), datetime.date(2017, 2, 21), datetime.date(2017, 2, 22), datetime.date(2017, 2, 23), datetime.date(2017, 2, 24), datetime.date(2017, 2, 25), datetime.date(2017, 2, 26)], [datetime.date(2017, 2, 27), datetime.date(2017, 2, 28), datetime.date(2017, 3, 1), datetime.date(2017, 3, 2), datetime.date(2017, 3, 3), datetime.date(2017, 3, 4), datetime.date(2017, 3, 5)]]
  month 3 : [[datetime.date(2017, 2, 27), datetime.date(2017, 2, 28), datetime.date(2017, 3, 1), datetime.date(2017, 3, 2), datetime.date(2017, 3, 3), datetime.date(2017, 3, 4), datetime.date(2017, 3, 5)], [datetime.date(2017, 3, 6), datetime.date(2017, 3, 7), datetime.date(2017, 3, 8), datetime.date(2017, 3, 9), datetime.date(2017, 3, 10), datetime.date(2017, 3, 11), datetime.date(2017, 3, 12)], [datetime.date(2017, 3, 13), datetime.date(2017, 3, 14), datetime.date(2017, 3, 15), datetime.date(2017, 3, 16), datetime.date(2017, 3, 17), datetime.date(2017, 3, 18), datetime.date(2017, 3, 19)], [datetime.date(2017, 3, 20), datetime.date(2017, 3, 21), datetime.date(2017, 3, 22), datetime.date(2017, 3, 23), datetime.date(2017, 3, 24), datetime.date(2017, 3, 25), datetime.date(2017, 3, 26)], [datetime.date(2017, 3, 27), datetime.date(2017, 3, 28), datetime.date(2017, 3, 29), datetime.date(2017, 3, 30), datetime.date(2017, 3, 31), datetime.date(2017, 4, 1), datetime.date(2017, 4, 2)]]
 row 2 :
  month 1 : [[datetime.date(2017, 3, 27), datetime.date(2017, 3, 28), datetime.date(2017, 3, 29), datetime.date(2017, 3, 30), datetime.date(2017, 3, 31), datetime.date(2017, 4, 1), datetime.date(2017, 4, 2)], [datetime.date(2017, 4, 3), datetime.date(2017, 4, 4), datetime.date(2017, 4, 5), datetime.date(2017, 4, 6), datetime.date(2017, 4, 7), datetime.date(2017, 4, 8), datetime.date(2017, 4, 9)], [datetime.date(2017, 4, 10), datetime.date(2017, 4, 11), datetime.date(2017, 4, 12), datetime.date(2017, 4, 13), datetime.date(2017, 4, 14), datetime.date(2017, 4, 15), datetime.date(2017, 4, 16)], [datetime.date(2017, 4, 17), datetime.date(2017, 4, 18), datetime.date(2017, 4, 19), datetime.date(2017, 4, 20), datetime.date(2017, 4, 21), datetime.date(2017, 4, 22), datetime.date(2017, 4, 23)], [datetime.date(2017, 4, 24), datetime.date(2017, 4, 25), datetime.date(2017, 4, 26), datetime.date(2017, 4, 27), datetime.date(2017, 4, 28), datetime.date(2017, 4, 29), datetime.date(2017, 4, 30)]]
  month 2 : [[datetime.date(2017, 5, 1), datetime.date(2017, 5, 2), datetime.date(2017, 5, 3), datetime.date(2017, 5, 4), datetime.date(2017, 5, 5), datetime.date(2017, 5, 6), datetime.date(2017, 5, 7)], [datetime.date(2017, 5, 8), datetime.date(2017, 5, 9), datetime.date(2017, 5, 10), datetime.date(2017, 5, 11), datetime.date(2017, 5, 12), datetime.date(2017, 5, 13), datetime.date(2017, 5, 14)], [datetime.date(2017, 5, 15), datetime.date(2017, 5, 16), datetime.date(2017, 5, 17), datetime.date(2017, 5, 18), datetime.date(2017, 5, 19), datetime.date(2017, 5, 20), datetime.date(2017, 5, 21)], [datetime.date(2017, 5, 22), datetime.date(2017, 5, 23), datetime.date(2017, 5, 24), datetime.date(2017, 5, 25), datetime.date(2017, 5, 26), datetime.date(2017, 5, 27), datetime.date(2017, 5, 28)], [datetime.date(2017, 5, 29), datetime.date(2017, 5, 30), datetime.date(2017, 5, 31), datetime.date(2017, 6, 1), datetime.date(2017, 6, 2), datetime.date(2017, 6, 3), datetime.date(2017, 6, 4)]]
  month 3 : [[datetime.date(2017, 5, 29), datetime.date(2017, 5, 30), datetime.date(2017, 5, 31), datetime.date(2017, 6, 1), datetime.date(2017, 6, 2), datetime.date(2017, 6, 3), datetime.date(2017, 6, 4)], [datetime.date(2017, 6, 5), datetime.date(2017, 6, 6), datetime.date(2017, 6, 7), datetime.date(2017, 6, 8), datetime.date(2017, 6, 9), datetime.date(2017, 6, 10), datetime.date(2017, 6, 11)], [datetime.date(2017, 6, 12), datetime.date(2017, 6, 13), datetime.date(2017, 6, 14), datetime.date(2017, 6, 15), datetime.date(2017, 6, 16), datetime.date(2017, 6, 17), datetime.date(2017, 6, 18)], [datetime.date(2017, 6, 19), datetime.date(2017, 6, 20), datetime.date(2017, 6, 21), datetime.date(2017, 6, 22), datetime.date(2017, 6, 23), datetime.date(2017, 6, 24), datetime.date(2017, 6, 25)], [datetime.date(2017, 6, 26), datetime.date(2017, 6, 27), datetime.date(2017, 6, 28), datetime.date(2017, 6, 29), datetime.date(2017, 6, 30), datetime.date(2017, 7, 1), datetime.date(2017, 7, 2)]]
 row 3 :
  month 1 : [[datetime.date(2017, 6, 26), datetime.date(2017, 6, 27), datetime.date(2017, 6, 28), datetime.date(2017, 6, 29), datetime.date(2017, 6, 30), datetime.date(2017, 7, 1), datetime.date(2017, 7, 2)], [datetime.date(2017, 7, 3), datetime.date(2017, 7, 4), datetime.date(2017, 7, 5), datetime.date(2017, 7, 6), datetime.date(2017, 7, 7), datetime.date(2017, 7, 8), datetime.date(2017, 7, 9)], [datetime.date(2017, 7, 10), datetime.date(2017, 7, 11), datetime.date(2017, 7, 12), datetime.date(2017, 7, 13), datetime.date(2017, 7, 14), datetime.date(2017, 7, 15), datetime.date(2017, 7, 16)], [datetime.date(2017, 7, 17), datetime.date(2017, 7, 18), datetime.date(2017, 7, 19), datetime.date(2017, 7, 20), datetime.date(2017, 7, 21), datetime.date(2017, 7, 22), datetime.date(2017, 7, 23)], [datetime.date(2017, 7, 24), datetime.date(2017, 7, 25), datetime.date(2017, 7, 26), datetime.date(2017, 7, 27), datetime.date(2017, 7, 28), datetime.date(2017, 7, 29), datetime.date(2017, 7, 30)], [datetime.date(2017, 7, 31), datetime.date(2017, 8, 1), datetime.date(2017, 8, 2), datetime.date(2017, 8, 3), datetime.date(2017, 8, 4), datetime.date(2017, 8, 5), datetime.date(2017, 8, 6)]]
  month 2 : [[datetime.date(2017, 7, 31), datetime.date(2017, 8, 1), datetime.date(2017, 8, 2), datetime.date(2017, 8, 3), datetime.date(2017, 8, 4), datetime.date(2017, 8, 5), datetime.date(2017, 8, 6)], [datetime.date(2017, 8, 7), datetime.date(2017, 8, 8), datetime.date(2017, 8, 9), datetime.date(2017, 8, 10), datetime.date(2017, 8, 11), datetime.date(2017, 8, 12), datetime.date(2017, 8, 13)], [datetime.date(2017, 8, 14), datetime.date(2017, 8, 15), datetime.date(2017, 8, 16), datetime.date(2017, 8, 17), datetime.date(2017, 8, 18), datetime.date(2017, 8, 19), datetime.date(2017, 8, 20)], [datetime.date(2017, 8, 21), datetime.date(2017, 8, 22), datetime.date(2017, 8, 23), datetime.date(2017, 8, 24), datetime.date(2017, 8, 25), datetime.date(2017, 8, 26), datetime.date(2017, 8, 27)], [datetime.date(2017, 8, 28), datetime.date(2017, 8, 29), datetime.date(2017, 8, 30), datetime.date(2017, 8, 31), datetime.date(2017, 9, 1), datetime.date(2017, 9, 2), datetime.date(2017, 9, 3)]]
  month 3 : [[datetime.date(2017, 8, 28), datetime.date(2017, 8, 29), datetime.date(2017, 8, 30), datetime.date(2017, 8, 31), datetime.date(2017, 9, 1), datetime.date(2017, 9, 2), datetime.date(2017, 9, 3)], [datetime.date(2017, 9, 4), datetime.date(2017, 9, 5), datetime.date(2017, 9, 6), datetime.date(2017, 9, 7), datetime.date(2017, 9, 8), datetime.date(2017, 9, 9), datetime.date(2017, 9, 10)], [datetime.date(2017, 9, 11), datetime.date(2017, 9, 12), datetime.date(2017, 9, 13), datetime.date(2017, 9, 14), datetime.date(2017, 9, 15), datetime.date(2017, 9, 16), datetime.date(2017, 9, 17)], [datetime.date(2017, 9, 18), datetime.date(2017, 9, 19), datetime.date(2017, 9, 20), datetime.date(2017, 9, 21), datetime.date(2017, 9, 22), datetime.date(2017, 9, 23), datetime.date(2017, 9, 24)], [datetime.date(2017, 9, 25), datetime.date(2017, 9, 26), datetime.date(2017, 9, 27), datetime.date(2017, 9, 28), datetime.date(2017, 9, 29), datetime.date(2017, 9, 30), datetime.date(2017, 10, 1)]]
 row 4 :
  month 1 : [[datetime.date(2017, 9, 25), datetime.date(2017, 9, 26), datetime.date(2017, 9, 27), datetime.date(2017, 9, 28), datetime.date(2017, 9, 29), datetime.date(2017, 9, 30), datetime.date(2017, 10, 1)], [datetime.date(2017, 10, 2), datetime.date(2017, 10, 3), datetime.date(2017, 10, 4), datetime.date(2017, 10, 5), datetime.date(2017, 10, 6), datetime.date(2017, 10, 7), datetime.date(2017, 10, 8)], [datetime.date(2017, 10, 9), datetime.date(2017, 10, 10), datetime.date(2017, 10, 11), datetime.date(2017, 10, 12), datetime.date(2017, 10, 13), datetime.date(2017, 10, 14), datetime.date(2017, 10, 15)], [datetime.date(2017, 10, 16), datetime.date(2017, 10, 17), datetime.date(2017, 10, 18), datetime.date(2017, 10, 19), datetime.date(2017, 10, 20), datetime.date(2017, 10, 21), datetime.date(2017, 10, 22)], [datetime.date(2017, 10, 23), datetime.date(2017, 10, 24), datetime.date(2017, 10, 25), datetime.date(2017, 10, 26), datetime.date(2017, 10, 27), datetime.date(2017, 10, 28), datetime.date(2017, 10, 29)], [datetime.date(2017, 10, 30), datetime.date(2017, 10, 31), datetime.date(2017, 11, 1), datetime.date(2017, 11, 2), datetime.date(2017, 11, 3), datetime.date(2017, 11, 4), datetime.date(2017, 11, 5)]]
  month 2 : [[datetime.date(2017, 10, 30), datetime.date(2017, 10, 31), datetime.date(2017, 11, 1), datetime.date(2017, 11, 2), datetime.date(2017, 11, 3), datetime.date(2017, 11, 4), datetime.date(2017, 11, 5)], [datetime.date(2017, 11, 6), datetime.date(2017, 11, 7), datetime.date(2017, 11, 8), datetime.date(2017, 11, 9), datetime.date(2017, 11, 10), datetime.date(2017, 11, 11), datetime.date(2017, 11, 12)], [datetime.date(2017, 11, 13), datetime.date(2017, 11, 14), datetime.date(2017, 11, 15), datetime.date(2017, 11, 16), datetime.date(2017, 11, 17), datetime.date(2017, 11, 18), datetime.date(2017, 11, 19)], [datetime.date(2017, 11, 20), datetime.date(2017, 11, 21), datetime.date(2017, 11, 22), datetime.date(2017, 11, 23), datetime.date(2017, 11, 24), datetime.date(2017, 11, 25), datetime.date(2017, 11, 26)], [datetime.date(2017, 11, 27), datetime.date(2017, 11, 28), datetime.date(2017, 11, 29), datetime.date(2017, 11, 30), datetime.date(2017, 12, 1), datetime.date(2017, 12, 2), datetime.date(2017, 12, 3)]]
  month 3 : [[datetime.date(2017, 11, 27), datetime.date(2017, 11, 28), datetime.date(2017, 11, 29), datetime.date(2017, 11, 30), datetime.date(2017, 12, 1), datetime.date(2017, 12, 2), datetime.date(2017, 12, 3)], [datetime.date(2017, 12, 4), datetime.date(2017, 12, 5), datetime.date(2017, 12, 6), datetime.date(2017, 12, 7), datetime.date(2017, 12, 8), datetime.date(2017, 12, 9), datetime.date(2017, 12, 10)], [datetime.date(2017, 12, 11), datetime.date(2017, 12, 12), datetime.date(2017, 12, 13), datetime.date(2017, 12, 14), datetime.date(2017, 12, 15), datetime.date(2017, 12, 16), datetime.date(2017, 12, 17)], [datetime.date(2017, 12, 18), datetime.date(2017, 12, 19), datetime.date(2017, 12, 20), datetime.date(2017, 12, 21), datetime.date(2017, 12, 22), datetime.date(2017, 12, 23), datetime.date(2017, 12, 24)], [datetime.date(2017, 12, 25), datetime.date(2017, 12, 26), datetime.date(2017, 12, 27), datetime.date(2017, 12, 28), datetime.date(2017, 12, 29), datetime.date(2017, 12, 30), datetime.date(2017, 12, 31)]]

calendar 模块级函数

import calendar

# 返回周标题头,参数值表示显示的周几的字符限制
# 例如,n=1 时,周一为 M, n=2 为 Mo, n=3 时为 Mon
print '\nweekheader(2):'
print calendar.weekheader(2)
print '\nweekheader(3):'
print calendar.weekheader(3)

# 本地化的周几名字列表
print '\nday_name:'
print list(calendar.day_name)
# 本地化的周几的缩写名字列表
print '\nday_abbr:'
print list(calendar.day_abbr)

# 本地化的月名列表
print '\nmonth_name:'
print list(calendar.month_name)
# 本地化的月缩写名列表
print '\nmonth_abbr:'
print list(calendar.month_abbr)
weekheader(2):
Mo Tu We Th Fr Sa Su

weekheader(3):
Mon Tue Wed Thu Fri Sat Sun

day_name:
['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

day_abbr:
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

month_name:
['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

month_abbr:
['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

格式化的例子

prmonth() (print month) 函数格式化输出某年某月的日历信息。

import calendar

c = calendar.TextCalendar(calendar.MONDAY)
c.prmonth(2017, 6)
     June 2017
Mo Tu We Th Fr Sa Su
          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

使用 HTMLCalendarformatmonth() 可产生一个类似效果的表格(HTML 文本),用 HTML 标签组成,并且每个单元格设置了对应星期几的一个 CSS 类。

更多资源

参考

]]>
Postgresql Tips 2017-06-06T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/postgresql-tips 修改数据库的所有者
ALTER DATABASE name OWNER TO new_owner;

参考:https://stackoverflow.com/questions/4313323/how-to-change-owner-of-postgresql-database

修改数据库用户密码

ALTER USER username WITH PASSWORD 'password';
]]>
Python 2 标准库示例:4.2 datetime-日期与时间值的处理 2017-06-06T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/Python2Lib-dt-datetime 目的: 该模块提供了与日期和时间解析、格式化和算术运算相关的函数和类。

Python 版本: 2.3+。

datetime 模块的实现专注于对日期和时间组件属性的抽取,以进行输出的格式化和处理。

共有两种种类的日期和时间对象:naiveaware

aware 种类的对象含有时区、夏令时等时间调整量信息。而 naive 种类的对象没有这些信息,因此,它是表示一个 UTC 时间、本地时间也是某个时区的时间,解释完全在于程序。

如果需要 aware 种类的对象,datetimetime 对象都有一个可选的属性 tzinfo,可用来设置为一个抽象类 tzinfo 的某子类的一个实例。tzinfo 对象含有至 UTC 时间的偏移量、时区名、是否有夏令时等信息。要注意的是,datetime 模块中没有提供 tzinfo 的具体类。pytz 库提供了一些时区实现细节。

时间

时间由 time 类表示。time 实例具有 hour, minute, second, microsecond 及时区信息等属性。

import datetime

t = datetime.time(1, 2, 3)
print t
print 'hour:', t.hour
print 'minute:', t.minute
print 'second:', t.second
print 'microsecond:', t.microsecond
print 'tzinfo:', t.tzinfo
01:02:03
hour: 1
minute: 2
second: 3
microsecond: 0
tzinfo: None

time 类中还有表示一天中有效时间区间的常量。

import datetime

print 'Earliest:', datetime.time.min
print 'Latest:', datetime.time.max
print 'Resolution:', datetime.time.resolution
Earliest: 00:00:00
Latest: 23:59:59.999999
Resolution: 0:00:00.000001

可见,时间的最小精度是 1 微秒。因此当将浮点值传给 microsecond 参数时,Python 2.7 中会出现 TypeError,而之前的版本中会出现 DeprecationWarning 并自动转成一个整数。

import datetime

for m in [1, 0, 0.1, 0.6]:
    try:
        print '%02.1f:' % m, datetime.time(0, 0, 0, microsecond=m)
    except TypeError, err:
        print 'Error:', err
1.0: 00:00:00.000001
0.0: 00:00:00
0.1: Error: integer argument expected, got float
0.6: Error: integer argument expected, got float

日期

日期值由 date 类表示。其实例中有 year, month, day 等属性。可用 today() 获取当前的日期。

import datetime

today = datetime.date.today()
print today
print 'ctime:', today.ctime()
tt = today.timetuple()  # to struct_time
print 'tuple:'
print ' tm_year:', tt.tm_year
print ' tm_mon:', tt.tm_mon
print ' tm_mday:', tt.tm_mday
print ' tm_hour:', tt.tm_hour
print ' tm_min:', tt.tm_min
print ' tm_sec:', tt.tm_sec
print ' tm_wday:', tt.tm_wday
print ' tm_yday:', tt.tm_yday
print ' tm_isdst:', tt.tm_isdst
print 'ordinal:', today.toordinal()  # (year=1, month=1, day=1) -> 1
print 'year:', today.year
print 'month:', today.month
print 'day:', today.day
2017-06-05
ctime: Mon Jun  5 00:00:00 2017
tuple:
 tm_year: 2017
 tm_mon: 6
 tm_mday: 5
 tm_hour: 0
 tm_min: 0
 tm_sec: 0
 tm_wday: 0
 tm_yday: 156
 tm_isdst: -1
ordinal: 736485
year: 2017
month: 6
day: 5

也可以从时间戳或序号值(ordinal 值,其中 1 年 1 月 1 日的序号值为 1)。

import datetime
import time

o = 736485
print 'o:', o
print 'fromordinal(o):', datetime.date.fromordinal(o)

t = time.time()
print 't:', t
print 'fromtimestamp(t):', datetime.date.fromtimestamp(t)
o: 736485
fromordinal(o): 2017-06-05
t: 1496673680.68
fromtimestamp(t): 2017-06-05

类似 timedate 也是最大值,最小值等常量。

import datetime

print 'Earliest:', datetime.date.min
print 'Latest:', datetime.date.max
print 'Resolution:', datetime.date.resolution
Earliest: 0001-01-01
Latest: 9999-12-31
Resolution: 1 day, 0:00:00

通过替换现存 date 对象的组件也可创建一个新的 date 实例。

import datetime

d1 = datetime.date(2016, 6, 5)
print 'd1:', d1.ctime()
d2 = d1.replace(year=2017)
print 'd2:', d2.ctime()
d1: Sun Jun  5 00:00:00 2016
d2: Mon Jun  5 00:00:00 2017

timedelta

datetime 对象间的偏移动量用 timedelta 对象表示。timedelta 值在内部都归整为 days, seconds, microseconds 三个整数存储,即通过 weeks, hours, minutes 等参数传入的值也都会转换合并到上面的 3 个变量中存储。

归整化后,timedelta 值的表示就会唯一,同时,各变量的值区间为:

  • 0 <= microseconds < 1000000
  • 0 <= seconds < 3600*24 (the number of seconds in one day)
  • -999999999 <= days <= 999999999
import datetime

print 'microseconds:', datetime.timedelta(microseconds=1)
print 'milliseconds:', datetime.timedelta(milliseconds=1)
print 'seconds:', datetime.timedelta(seconds=1)
print 'minutes:', datetime.timedelta(minutes=1)
print "hours:", datetime.timedelta(hours=1)
print 'days:', datetime.timedelta(days=1)
print 'weeks:', datetime.timedelta(weeks=1)
microseconds: 0:00:00.000001
milliseconds: 0:00:00.001000
seconds: 0:00:01
minutes: 0:01:00
hours: 1:00:00
days: 1 day, 0:00:00
weeks: 7 days, 0:00:00

total_seconds() 返回 timedelta 实例的总秒数(浮点数):

import datetime

for delta in [datetime.timedelta(microseconds=1),
            datetime.timedelta(milliseconds=1),
            datetime.timedelta(seconds=1),
            datetime.timedelta(minutes=1),
            datetime.timedelta(hours=1),
            datetime.timedelta(days=1),
            datetime.timedelta(weeks=1),
            ]:
    print '%15s = %s seconds' % (delta, delta.total_seconds())
 0:00:00.000001 = 1e-06 seconds
 0:00:00.001000 = 0.001 seconds
        0:00:01 = 1.0 seconds
        0:01:00 = 60.0 seconds
        1:00:00 = 3600.0 seconds
 1 day, 0:00:00 = 86400.0 seconds
7 days, 0:00:00 = 604800.0 seconds

日期的算术运算

import datetime

today = datetime.date.today()
print 'today:', today

one_day = datetime.timedelta(days=1)
print 'one day:', one_day

yesterday = today - one_day
print 'yesterday:', yesterday

tomorrow = today + one_day
print 'tomorrow:', tomorrow

print
print 'tomorrow - yesterday:', tomorrow - yesterday
print 'yesterday - tomorrow:', yesterday - tomorrow
today: 2017-06-05
one day: 1 day, 0:00:00
yesterday: 2017-06-04
tomorrow: 2017-06-06

tomorrow - yesterday: 2 days, 0:00:00
yesterday - tomorrow: -2 days, 0:00:00

比较

datetime 值都可以进行比较。

import datetime
import time

print 'Times:'
t1 = datetime.time(12, 55, 0)
print ' t1:', t1
t2 = datetime.time(13, 5, 0)
print ' t2:', t2
print ' t1 < t2', t1 < t2

print '\nDates:'
d1 = datetime.date.today()
print ' d1:', d1
d2 = datetime.date.today() + datetime.timedelta(days=1)
print ' d2:', d2
print ' d1 > d2:', d1 > d2
Times:
 t1: 12:55:00
 t2: 13:05:00
 t1 < t2 True

Dates:
 d1: 2017-06-05
 d2: 2017-06-06
 d1 > d2: False

组合日期和时间

datetime 类组合了 datetime 的值,因而包含了它们两个的所有属性。 有以下几个创建 datetime 实例的便捷方法。

import datetime

# 有可选 tzinfo 参数,没有提供时返回本地的时间,此时同 today()
print 'now:', datetime.datetime.now()

# 返回本地的时间
print 'today:', datetime.datetime.today()

# 返回 UTC 时间
print 'UTC now:', datetime.datetime.utcnow()
print

fields = ['year', 'month', 'day',
         'hour', 'minute', 'second', 'microsecond',
         ]

d = datetime.datetime.now()
for attr in fields:
    print '%15s: %s' % (attr, getattr(d, attr))
now: 2017-06-05 23:13:24.028225
today: 2017-06-05 23:13:24.029239
UTC now: 2017-06-05 15:13:24.029494

           year: 2017
          month: 6
            day: 5
           hour: 23
         minute: 13
         second: 24
    microsecond: 29915

date 类似,datetime 也可以通过 fromordinal()fromtimestamp() 创建实例。同时,通过 combine 可以将 datetime 值合并成一个 datetime

import datetime

t = datetime.time(1, 2, 3)
print 't:', t

d = datetime.date.today()
print 'd:', d

dt = datetime.datetime.combine(d, t)
print 'dt:', dt
t: 01:02:03
d: 2017-06-05
dt: 2017-06-05 01:02:03

格式化和解析

datetime 对象默认使用 ISO-8601 格式(YYYY-MM-DDTHH:MM:SS.mmmmmm)输出字符串表示。可使用 strftime() 进行其它格式化输出。用 strptime() 可将格式化字符串转换回 datetime 对象。详细的格式化指令见: datetime 的官方文档

import datetime

format = "%Y-%m-%d %H:%M:%S"

today = datetime.datetime.today()
print 'ISO:', today

s = today.strftime(format)
print 'strftime:', s

d = datetime.datetime.strptime(s, format)
print 'strptime:', d.strftime(format)
ISO: 2017-06-05 23:23:43.133657
strftime: 2017-06-05 23:23:43
strptime: 2017-06-05 23:23:43

更多资源

参考

]]>
Git Tips 2017-06-05T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/git-tips bitbucket fatal: The remote end hung up unexpectedly

现象: git clone 结束时出现:

remote: Compressing objects: 100% (6224/6224), done.
Connection to bitbucket.org closed by remote host.9.00 KiB/s    
fatal: The remote end hung up unexpectedly
fatal: early EOF
fatal: index-pack failed

方案:

It seems like the commit size was too big (default is < 1Mbyte).

Resolved it with rising the limit up to 500Mbytes:

$ git config --global http.postBuffer 524288000

同步 push 到多个镜像库

  1. 创建多个镜像 remote:
$ git remote add mirror git@git.sitename.com:username/mirror_rep.git
  1. push
$ git push && git push mirror

see: https://bitbucket.org/site/master/issues/3578/cannot-push-fatal-the-remote-end-hung-up

]]>
Python 2 标准库示例:4.1 time-时钟时间 2017-06-05T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/Python2Lib-dt-time 不像 int, float 等,Python 没有本地的日期或时间类型,但包含有 3 个模块用于处理与日期和时间相关的值。

  • time 模块导出底层 C 函数库中时间相关的函数,包括获取时钟时间和处理器运行时间的函数,基本的解析和格式化工具等。
  • datetime 模块为日期、时间及其组合支持更高级的接口。该模块中的类还支持算术操作、比较、时区配置等。
  • calendar 模块为年、月、周等创建格式化表示。

目的: 提供处理时钟时间的函数。

Python 版本: 1.4+

time 模块与低层的 C 实现关联,因此一些细节会特定于平台。

术语

纪元 epoch

指开始的时间点,在 Unix 中,指 1970-01-01 00:00:00。可通过 time.gmtime(0) 获取。本模块中的函数不能处理纪元前或纪元很久后的时间。表示的时间范围由低层的 C 实现决定,Unix 中最大表示时间是 2038 年。

UTC,GMT

即世界标准时间 Coordinated Universal Time,缩写为 UTC 不是误写,是英文与法文折中的结果。之前叫作格林尼治标准时间(Greenwich Mean Time, GMT)。

DST

DST 是 Daylight Saving Time 的缩写, 即夏令时间(日光节省时间)。由于夏天日出早,白天长,一些地方人为规定将时间提前 1 小时,从而使人们早睡早起,节约能源。实行夏令时的地方,处理时间时要偏移 1 个小时。DST 的规则由各地方规定,且可能每年变化,只能通过查询 C 库中包含的一个表格来了解。

struct_time

gmtime(), localtime(), strptime() 返回的都是 struct_time 类型的时间值,这是一个具有 named tuple 接口的对象,各项可通过索引和属性名访问。

索引 属性名
0 tm_year 例如 2017
1 tm_mon [1, 12]
2 tm_mday [1, 31]
3 tm_hour [0, 23]
4 tm_min [0, 59]
5 tm_sec [0, 61]
6 tm_wday [0, 6], 0 指周一
7 tm_yday [1, 366]
8 tm_isdst 0, 1, -1

其中 tm_sec 最多有 61 秒,这是由于闰秒(leap second)的存在导致的。由于地球自转不均,会使世界时(民用时)和原子时有偏差,故要人为规定在某些时间调整世界标准间约为 1 秒。

asctime(), mktime(), strftime() 接受的参数也是 struct_time 对象。

自纪元以前的浮点秒数值与 struct_time 对象间的转换如下:

From To Use
seconds since epoch struct_time in UTC gmtime()
seconds since epoch struct_time in local time localtime()
struct_time in UTC seconds since epoch calendar.timegm()
struct_time in local time seconds since epoch mktime

挂钟时间

time 模块中的一个核心函数是 time(),它返回一个浮点值,表示从纪元时间到当前的秒数。

import time

print 'The time is:', time.time()
The time is: 1496577423.91

虽然返回值是浮点数,但具体精度依赖平台。浮点表示方便排序和比较操作,但不易看懂。ctime() (char time 缩写)可将当前值或浮点数表示的时间值格式化输出。

import time

print 'The time is:', time.ctime()
latter = time.time() + 15
print '15 secs from now:', time.ctime(latter)
The time is: Sun Jun  4 21:03:16 2017
15 secs from now: Sun Jun  4 21:03:31 2017

处理器时钟时间

time() 返回挂钟时间,而 clock() 返回处理器时钟时间,其返回值反映了程序使用处理器的实际时间,故可用于性能测试、基准测试等。

import hashlib
import time

# Data to use to calculate md5 checksums
data = 'abc'*1000

for i in range(5):
    h = hashlib.sha1()
    print time.ctime(), ': %0.3f %0.3f' % (time.time(), time.clock())
    for i in range(300000):
        h.update(data)
    cksum = h.digest()
Sun Jun  4 21:08:51 2017 : 1496581731.747 0.924
Sun Jun  4 21:08:53 2017 : 1496581733.464 2.636
Sun Jun  4 21:08:55 2017 : 1496581735.176 4.347
Sun Jun  4 21:08:56 2017 : 1496581736.860 6.031
Sun Jun  4 21:08:58 2017 : 1496581738.538 7.709

当程序没有做任何事时,处理器一般不会计时。如下,sleep() 会交出当前线程的控制权,并等待系统再次调度它,其 sleep 时间没有计时在程序运行时间内。

import time

for i in range(6, 1, -1):
    print '%s %0.2f %0.2f' % (time.ctime(),
                             time.time(),
                             time.clock())
    print 'Sleeping', i
    time.sleep(i)
Sun Jun  4 21:11:36 2017 1496581896.07 9.41
Sleeping 6
Sun Jun  4 21:11:42 2017 1496581902.08 9.42
Sleeping 5
Sun Jun  4 21:11:47 2017 1496581907.08 9.42
Sleeping 4
Sun Jun  4 21:11:51 2017 1496581911.09 9.42
Sleeping 3
Sun Jun  4 21:11:54 2017 1496581914.09 9.43
Sleeping 2

时间组件

通过 struct_time 定义时间的各组件部分。

import time

def show_struct(s):
    print ' tm_year:', s.tm_year
    print ' tm_mon:', s.tm_mon
    print ' tm_mday:', s.tm_mday
    print ' tm_hour:', s.tm_hour
    print ' tm_min:', s.tm_min
    print ' tm_sec:', s.tm_sec
    print ' tm_wday:', s.tm_wday
    print ' tm_yday:', s.tm_yday
    print ' tm_isdst:', s.tm_isdst
    
print 'gmtime(UTC):'
show_struct(time.gmtime())
print '\nlocaltime:'
show_struct(time.localtime())
print '\nmktime:', time.mktime(time.localtime())
gmtime(UTC):
 tm_year: 2017
 tm_mon: 6
 tm_mday: 4
 tm_hour: 13
 tm_min: 19
 tm_sec: 11
 tm_wday: 6
 tm_yday: 155
 tm_isdst: 0

localtime:
 tm_year: 2017
 tm_mon: 6
 tm_mday: 4
 tm_hour: 21
 tm_min: 19
 tm_sec: 11
 tm_wday: 6
 tm_yday: 155
 tm_isdst: 0

mktime: 1496582351.0

gmtime() 返回 UTC 的当前时间,localtime() 返回当前时区内的当前时间,返回的都是 struct_time 对象。而 mktime()struct_time 值转换成一个浮点数时间值。

时区

检测当前时间的函数依赖时区设置信息。时区可由程序设置,也可使用系统默认的时间设置。修改时区设置不会修改时间值,只是修改了时间的呈现方式。

要修改时区,先设置环境变量 TZ,再调用 tzset() 即可。时区的细节很复杂,因此通常是通过指定时区名,由低层库进行具体处理。

import time
import os

def show_zone_info():
    print ' TZ:', os.environ.get('TZ', '(not set)')
    print ' tzname:', time.tzname # return (name_of_local_non-DST_timezone, name_of_local-DST_timezone)
    print ' Zone: %d (%d)' % (time.timezone, # offset of the local non-DST timezone, in seconds
                             (time.timezone/3600))
    print ' DST:', time.daylight
    print ' Time:', time.ctime()
    
print 'Default:'
show_zone_info()

ZONES = [ 'GMT',
          'Europe/Amsterdam',
        ]

for zone in ZONES:
    os.environ['TZ'] = zone
    time.tzset()
    print zone, ":"
    show_zone_info()
Default:
 TZ: Europe/Amsterdam
 tzname: ('CET', 'CEST')
 Zone: -3600 (-1)
 DST: 1
 Time: Sun Jun  4 15:43:05 2017
GMT :
 TZ: GMT
 tzname: ('GMT', 'GMT')
 Zone: 0 (0)
 DST: 0
 Time: Sun Jun  4 13:43:05 2017
Europe/Amsterdam :
 TZ: Europe/Amsterdam
 tzname: ('CET', 'CEST')
 Zone: -3600 (-1)
 DST: 1
 Time: Sun Jun  4 15:43:05 2017

时间的解析和格式化

strptime() (p 为 parse) 和 strftime() (f 为 format) 在 struct_time 与字符串表示的时间值之间进行转换。具体的格式指令见 time 模块的官方文档

下例将当前时间从字符串转成 struct_time 实例,再转回字符串。

import time

def show_struct(s):
    print ' tm_year:', s.tm_year
    print ' tm_mon:', s.tm_mon
    print ' tm_mday:', s.tm_mday
    print ' tm_hour:', s.tm_hour
    print ' tm_min:', s.tm_min
    print ' tm_sec:', s.tm_sec
    print ' tm_wday:', s.tm_wday
    print ' tm_yday:', s.tm_yday
    print ' tm_isdst:', s.tm_isdst
    
now = time.ctime()
print 'Now:', now

parsed = time.strptime(now)
print '\nParsed:'
show_struct(parsed)

print '\nFormatted:', time.strftime("%a %b %d %H:%M:%S %Y", parsed)
Now: Sun Jun  4 15:50:50 2017

Parsed:
 tm_year: 2017
 tm_mon: 6
 tm_mday: 4
 tm_hour: 15
 tm_min: 50
 tm_sec: 50
 tm_wday: 6
 tm_yday: 155
 tm_isdst: -1

Formatted: Sun Jun 04 15:50:50 2017

输入字符串与输入不完全相同,输出中的月日期加了 0 前缀。

更多资源

参考

]]>
Python 2 标准库示例:3.4 contextlib-上下文管理工具 2017-06-04T00:00:00+08:00 Haiiiiiyun haiiiiiyun.github.io/Python2Lib-algo-contextlib 目的: 创建和使用上下文管理器的工具。

Python 版本: 2.5+。

contextlib 模块结合 with 语句使用上下文管理器。由于 with 语句在 Python 2.6 中引入,若要在 Python 2.5 中使用,则需要从 __future__ 中导入。

Context Manager API

一个 context manager 负责一个代码段中的一个资源,它在当进入代码段时创建,并在退出代码段时被清除。例如,文件对象支持 context manager API,从而确保了如下的代码段中,当完成读写操作后,文件会被自动关闭。

with open('/tmp/pymotw.txt', 'wt') as f:
    f.write('contents go here')
# file is automatically closed

上下文管理器对象通过 with 语句启用,主要有 2 个方法。__enter()__ 方法在执行流进入 with 的代码段时运行,它返回的一个对象会在当前上下文中使用。当执行流离开代码段时,会调用 __exit__() 来清理使用的资源。

class Context(object):
    def __init__(self):
        print '__init__()'
        
    def __enter__(self):
        print '__enter__()'
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print '__exit__()'
        
with Context():
    print 'Doing work in the context'
__init__()
__enter__()
Doing work in the context
__exit__()

__enter__() 方法返回的对象,可通过 with 语句的 as 从句关联到一个变量,如下。

class WithinContext(object):
    def __init__(self, context):
        print 'WithinContext.__init__(%s)' % context
        
    def do_something(self):
        print 'WithinContext.do_something()'
        
    def __del__(self):
        print 'WithinContext.__del__'
        
class Context(object):
    def __init__(self):
        print 'Context.__init__()'
        
    def __enter__(self):
        print 'Context.__enter__()'
        return WithinContext(self)
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print 'Context.__exit__()'
        
with Context() as c:
    c.do_something()
Context.__init__()
Context.__enter__()
WithinContext.__init__(<__main__.Context object at 0x7fdbc327cfd0>)
WithinContext.do_something()
Context.__exit__()

如果 with 代码段中抛出异常, 则 __exit__() 方法的参数会接收到异常的详细信息。

class Context(object):
    def __init__(self, handle_error):
        print '__init__(%s)' % handle_error
        self.handle_error = handle_error
        
    def __enter__(self):
        print '__enter__()'
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print '__exit__()'
        print ' exc_type=', exc_type
        print ' exc_val=', exc_val
        print ' exc_tb=', exc_tb
        return self.handle_error
    
with Context(True):
    raise RuntimeError('error message handled')
    
print

with Context(False):
    raise RuntimeError('error message propagated')
__init__(True)
__enter__()
__exit__()
 exc_type= <type 'exceptions.RuntimeError'>
 exc_val= error message handled
 exc_tb= <traceback object at 0x7fdbadeeab00>

__init__(False)
__enter__()
__exit__()
 exc_type= <type 'exceptions.RuntimeError'>
 exc_val= error message propagated
 exc_tb= <traceback object at 0x7fdbadeeac68>



---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

<ipython-input-5-45d0ff46304f> in <module>()
     21 
     22 with Context(False):
---> 23     raise RuntimeError('error message propagated')


RuntimeError: error message propagated

__exit__() 返回 True 表示已处理了异常,不向上传递异常,而返回 False 表示会向上传递异常。

用 generator 实现上下文管理器

定义上下文管理器的传统方法是实现 __enter__()__exit__()。但也可以通过 contextmanager() 装饰器将一个 generator 函数直接转变成一个上下文管理器。

import contextlib

@contextlib.contextmanager
def make_context():
    print ' entering'            # __enter__
    try:
        yield {}                 # return from __enter__
    except RuntimeError, err:    
        print ' Error:', err     # like __exit__ return True, not to propagate.
    finally:
        print ' exiting'         # __exit__
        
print 'Normal:'
with make_context() as value:
    print ' inside with statement:', value
    
print '\nHandled error:'
with make_context() as value:
    raise RuntimeError('showing example of handling an error')
    
print '\nUnhandled error:'
with make_context() as value:
    raise ValueError('this exception is not handled')
Normal:
 entering
 inside with statement: {}
 exiting

Handled error:
 entering
 Error: showing example of handling an error
 exiting

Unhandled error:
 entering
 exiting



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-7-ee829d1776a7> in <module>()
     21 print '\nUnhandled error:'
     22 with make_context() as value:
---> 23     raise ValueError('this exception is not handled')


ValueError: this exception is not handled

generatoryield 前的代码用来初始化上下文管理器,并只 yield 一次,yield 出的值相当于 __enter__ 的返回值。try, except 中提及的异常会被处理,不会向上传递,即等同 __exit__ 返回 True。

嵌套上下文

使用 nested() 可在一个 with 语句中使用嵌套的上下文。

import contextlib

@contextlib.contextmanager
def make_context(name):
    print 'entering:', name
    yield name
    print 'exiting:', name
    
with contextlib.nested(make_context('A'),
                      make_context('B')) as (A, B):
    print 'inside with statement:', A, B
entering: A
entering: B
inside with statement: A B
exiting: B
exiting: A


/usr/local/lib/python2.7/dist-packages/ipykernel_launcher.py:10: DeprecationWarning: With-statements now directly support multiple context managers
  # Remove the CWD from sys.path while we load stuff.

上例中,注意到进入上下文的顺序和离开的正好相反。在 Python 2.7 中, nested() 已过时,因为 with 现已直接支持嵌套,如下:

import contextlib

@contextlib.contextmanager
def make_context(name):
    print 'entering:', name
    yield name
    print 'exiting:', name
    
with make_context('A') as A, make_context('B') as B:
    print 'inside with statement:', A, B
entering: A
entering: B
inside with statement: A B
exiting: B
exiting: A

关闭打开的句柄

file 类支持 context manager API,但有些旧的类如 urllib.urlopen() 返回的对象,都是用 close() 方法关闭,但不支持 context manager API

可以使用 closing() 为这些对象创建一个上下文,确保退出时会调用 close()

import contextlib

class Door(object):
    def __init__(self):
        print ' __init__()'
        
    def close(self):
        print ' close()'
        
print 'Normal Example:'
with contextlib.closing(Door()) as door:
    print ' inside with statement'
    
print '\nError handling example:'
try:
    with contextlib.closing(Door()) as door:
        print ' rasing from inside with statement'
        raise RuntimeError('error message')
except Exception, err:
    print ' Had an error:', err
Normal Example:
 __init__()
 inside with statement
 close()

Error handling example:
 __init__()
 rasing from inside with statement
 close()
 Had an error: error message

更多资源

参考

]]>