Python知识点3

文件操作

打开文件

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

file:打开的文件名称。

mode:打开文件后的处理方式。模式见下表。

buffering:读写文件的缓存模式。0表示不缓存,1表示缓存,如大于1则表示缓冲区大小。默认值是缓存模式。

encoding:对文本进行编码和解码的方式,只适用于文本模式,可以使用Python支持的任何格式,如GBK、utf8、CP936等。

模式 说明
r 读模式(默认模式,可省略),如果文件不存在则抛出异常
w 写模式,如果文件已存在,先清空原有内容
x 写模式,创建新文件,如果文件已存在则抛出异常
a 追加模式,不覆盖文件中原有内容
b 二进制模式(可与其他模式组合使用)
t 文本模式(默认模式,可省略)
+ 读、写模式(可与其他模式组合使用)

上下文管理with语句

1
2
3
4
5
with open(filename, mode, encoding) as fp:
# 这里写通过文件对象fp读写文件内容的语句

with open('test.txt', 'r') as src, open('test_new.txt', 'w') as dst:
dst.write(src.read())

文件对象常用属性

属性 说明
buffer 返回当前文件的缓冲区对象
closed 判断文件是否关闭,若文件已关闭则返回True
fileno 文件号,一般不需要太关心这个数字
mode 返回文件的打开模式
name 返回文件的名称

文件对象常用方法

方法 功能说明
close() 把缓冲区的内容写入文件,同时关闭文件,并释放文件对象
detach() 分离并返回底层的缓冲,底层缓冲被分离后,文件对象不再可用,不允许做任何操作
flush() 把缓冲区的内容写入文件,但不关闭文件
read([size]) 从文本文件中读取size个字符(Python 3.x)的内容作为结果返回,或从二进制文件中读取指定数量的字节并返回,如果省略size则表示读取所有内容
readable() 测试当前文件是否可读
readline() 从文本文件中读取一行内容作为结果返回
readlines() 把文本文件中的每行文本作为一个字符串存入列表中,返回该列表,对于大文件会占用较多内存,不建议使用
seek(offset[, whence]) 把文件指针移动到新的字节位置,offset表示相对于whence的位置。whence为0表示从文件头开始计算,1表示从当前位置开始计算,2表示从文件尾开始计算,默认为0
seekable() 测试当前文件是否支持随机访问,如果文件不支持随机访问,则调用方法seek()、tell()和truncate()时会抛出异常
tell() 返回文件指针的当前位置
truncate([size]) 删除从当前指针位置到文件末尾的内容。如果指定了size,则不论指针在什么位置都只留下前size个字节,其余的一律删除
write(s) 把s的内容写入文件
writable() 测试当前文件是否可写
writelines(s) 把字符串列表写入文本文件,不添加换行符

序列化

所谓序列化,简单地说就是把内存中的数据在不丢失其类型信息的情况下转成对象的二进制形式的过程,对象序列化后的形式经过正确的反序列化过程应该能够准确无误地恢复为原来的对象。

Python中常用的序列化模块有struct、pickle、marshal和shelve。

pickle模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pickle

i = 13000000
a = 99.056
s = '中国人民123abc'
lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
tu = (-5, 10, 8)
coll = {4, 5, 6}
dic = {'a':'apple', 'b':'banana', 'g':'grape', 'o':'orange'}
data = [i, a, s, lst, tu, coll, dic]

with open('sample_pickle.dat', 'wb') as f:
try:
pickle.dump(len(data), f) #表示后面将要写入的数据个数
for item in data:
pickle.dump(item, f)
except:
print('写文件异常!') #如果写文件异常则跳到此处执行

with open('sample_pickle.dat', 'rb') as f:
n = pickle.load(f) #读出文件的数据个数
for i in range(n):
x = pickle.load(f)
print(x)

struct模块

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
import struct

n = 1300000000
x = 96.45
b = True
s = 'a1@中国'
sn = struct.pack('if?', n, x, b) # 序列化
length = len(sn) # 序列化后的长度,第14行会用到
with open('sample_struct.dat', 'wb') as fp:
fp.write(sn) # 写入字节串
fp.write(s.encode()) # 字符串直接编码为字节串写入

with open('sample_struct.dat', 'rb') as fp:
sn = fp.read(length) # 读length个字节
tu = struct.unpack('if?', sn)
print(tu)
n, x, bl = tu
print('n=', n)
print('x=', x)
print('bl=', bl)
s = fp.read(9).decode()
print('s=', s)

'''
(1300000000, 96.44999694824219, True)
n= 1300000000
x= 96.44999694824219
bl= True
s= a1@中国
'''

Python知识点2

函数 function

默认值参数

默认值参数只在函数定义时被解释一次,可以使用 函数名.__defaults__ 查看所有默认参数的当前值。

1
2
3
4
def f(x=3):
pass

print(f.__defaults__) # (3,)

可变长度参数、序列解包

可变长度参数:可变长度参数主要有两种形式:在参数名前加1个*或2个**

  • *parameter用来接收多个位置实参并将其放在一个元组

  • **parameter接收多个关键参数并存放到字典

传递参数时,可以通过在实参序列前加一个星号将其解包,然后传递给多个单变量形参。

1
2
3
4
5
def demo(a, b, c):
print(a + b + c)

seq = [1, 2, 3]
demo(*seq)

如果函数实参是字典,可以在前面加两个星号进行解包,等价于关键参数。

1
2
3
4
5
def demo(a, b, c):
print(a + b + c)

dic = {'a': 1, 'b': 2, 'c': 3}
demo(**dic)

调用函数时对实参序列使用一个星号*进行解包后的实参将会被当做普通位置参数对待,并且会在关键参数和使用两个星号**进行序列解包的参数之前进行处理。

序列解包(的位置)不能在关键参数解包之后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
demo(a=1, *(2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: demo() got multiple values for argument 'a'

demo(b=1, *(2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: demo() got multiple values for argument 'b'

demo(c=1, *(2, 3)) # ok

demo(**{'a': 1, 'b': 2}, *(3,))
File "<stdin>", line 1
SyntaxError: iterable argument unpacking follows keyword argument unpacking

demo(*(3,), **{'a': 1, 'b': 2})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: demo() got multiple values for argument 'a'

demo(*(3,), **{'c':1, 'b':2}) # ok

变量作用域

局部变量

在某个作用域内任意位置只要有为变量赋值的操作,该变量在这个作用域内就是局部变量,除非使用 global 进行了声明。

1
2
3
4
5
6
7
8
9
10
11
12
x = 3

def f():
print(x)
x = 5
print(x)

f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment

多模块间共享变量

如果需要在同一个程序的不同模块之间共享全局变量的话,可以编写一个专门的模块来实现这一目的。例如,假设在模块A.py中有如下变量定义:

1
global_variable = 0

则可以在其他模块中包含以下用来设置/获取全局变量的语句:

1
2
3
import A
A.global_variable = 1
print(A.global_variable)

nonlocal变量

除了局部变量和全局变量,Python还支持使用 nonlocal 关键字定义一种介于二者之间的变量。关键字 nonlocal 声明的变量会引用距离最近的非全局作用域的变量,要求声明的变量已经存在,关键字 nonlocal 不会创建新变量

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
def scope_test():
def do_local():
var = "local variable"

def do_nonlocal():
nonlocal var # 要求var必须是已存在的变量
var = "nonlocal variable"

def do_global():
global var # 如果全局作用域内没有var,就自动新建一个
var = "global variable"

var = "original"
do_local()
print("after do_local:", var)
do_nonlocal()
print("after do_nonlocal:", var)
do_global()
print("after_do_global:", var)

scope_test()
print("global:", var)

# after do_local: original
# after do_nonlocal: nonlocal variable
# after_do_global: nonlocal variable
# global: global variable

变量访问时的搜索顺序

变量访问时的LEGB顺序:Local ==> Enclosing ==> Global ==> Builtin

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
x = 3
def outer():
y = 5

# 这个自定义函数和内置函数名字相同,会在当前作用域和更内层作用域中影响内置函数map()的正常使用
def map():
return 'fake map()'

def inner():
x = 7
y = 9
# 最内层的作用域内,局部变量(Local)x,y优先被访问
# 在局部域、闭包、全局作用域内都不存在函数max,最后在内置作用域(Builtin)内搜索到函数max
# 当前作用域中不存在map,但在外层的闭包作用域内搜索到了,并没有调用内置函数map
print('inner:', x, y, max(x,y), map())

inner()
# 在当前作用域(闭包,Enclosing)内,y可以直接访问
# 当在当前作用域内不存在x,继续到全局作用域(Global)去搜索
# 当前作用域内不存在函数max,外层全局作用域也不存在,最后在内置作用域(Builtin)内搜索到函数max
# 当前作用域中有个map,直接调用了,没有调用内置函数map
print('outer:', x, y, max(x,y), map())

outer()
# 当前作用域中有x,可以直接访问,但不存在y
# 由于当前处于全局作用域,按Python变量搜索顺序,会继续在内置作用域搜索
# 不会去搜索Enclosing和Local作用域,但在内置作用域内也不存在y,代码引发异常
print('outside:', x, y, max(x,y))

生成器函数 generator

包含yield语句的函数可以用来创建生成器对象,这样的函数也称生成器函数。

每次执行到yield语句会返回一个值然后暂停或挂起后面代码的执行,下次通过生成器对象的__next__()方法、内置函数next()、for循环遍历生成器对象元素或其他方式显式“索要”数据时恢复执行。

生成器对象具有惰性求值的特点,适合大数据处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def f():
for i in range(10):
yield i

try:
a = f()
print(a)
while True:
print(next(a), end = ' ')
except StopIteration:
pass

# <generator object f at 0x0000017AFC12BA50>
# 0 1 2 3 4 5 6 7 8 9
1
2
3
4
5
6
7
8
9
10
11
12
13
def f():
yield from 'abcdefghijklmnopqrstuvwxyz'

try:
a = f()
print(a)
while True:
print(next(a), end = ' ')
except StopIteration:
pass

# <generator object f at 0x000002B26498BA50>
# a b c d e f g h i j k l m n o p q r s t u v w x y z
1
2
3
4
5
6
7
def f():
yield 1
yield 2
yield 3

a, b, c = f()
print(a, b, c)

装饰器 decorator

修饰器(decorator)是函数嵌套定义的另一个重要应用。修饰器本质上也是一个函数,只不过这个函数接收其他函数作为参数并对其进行一定的改造之后返回新函数。

Python面向对象程序设计中的静态方法、类方法、属性等也都是通过修饰器实现的,Python中还有很多这样的用法。

下面的代码演示了修饰器的定义与使用方法,定义其他函数调用之前或之后需要执行的通用代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def before(func):
def wrapper(*args, **kwargs):
print('Before function')
return func(*args, **kwargs)
return wrapper

def after(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print('After function')
return result
return wrapper

@before
@after
def test():
print(3)

test()

# Before function
# 3
# After function

偏函数/函数柯里化

偏函数(partial function)和函数柯里化(function currying)是函数式编程中常用的技术。有时候我们在复用已有函数时可能需要固定其中的部分参数,这除了可以通过默认值参数来实现之外,还可以使用偏函数。例如,有个函数用来实现3个数字相加:

1
2
def add3(a, b, c):
return a + b + c

如果现在需要一个类似的函数,与上面的函数add3()的区别仅在于参数b固定为一个数字(例如3),这时就可以使用偏函数的技术来复用上面的函数。

1
2
3
4
def add2(a, c):
return add3(a, 3, c)

print(add2(1, 1))

或者使用标准库functools提供的partial()方法创建指定函数的偏函数。

1
2
3
4
from functools import partial

add2 = partial(add3, b=3)
print(add2(a=1, c=1))

Python知识点1

基本知识

Python对象类型

对象类型 对应类型 实例 解释
数字 int, float, complex 1234, 3.14, 1.3e5, 3+4j 数字大小没有限制,内置支持复数及其运算。
字符串 str ‘abcd’, “abcd”, ‘’’ab’’’, “””ab”””, r’abc’, R’abc’ 不可变,使用单引号、双引号、三引号作为定界符,或以字母r或R引导的原始字符串。
单引号、双引号、三单引号、三双引号可以互相嵌套,用来表示复杂字符串。
三引号表示的字符串可以换行,支持排版较为复杂的字符串;三引号还可以在程序中表示较长的注释。
字符串 bytes b’Hello World’ 不可变,以字母b引导,使用单引号、双引号、三引号作为定界符
列表 list [1, 2, 3], [[1, 2, 3], None] 所有元素放在一对方括号[]中,元素之间使用逗号分隔,其中的元素可以是任意类型
字典 dict {1: “one”, 2: “two”} 所有元素放在一对大括号{}中,元素之间使用逗号分隔,元素形式为键:值。字典由于可变,所以本身不可哈希
元组 tuple (2, 3, 4), (-3,) 不可变,所有元素放在一对圆括号()中,元素之间使用逗号分隔,如果元组中只有一个元素的话,该元素后面需加一个逗号
集合 set, frozenset {‘a’, ‘b’, ‘c’}, frozenset({‘a’, ‘b’, ‘c’}) 所有元素放在一对大括号{}中,元素之间使用逗号分隔,元素不允许重复。set可变,而frozenset不可变。set由于可变,故本身不可哈希;frozenset可以
布尔 bool True, False 逻辑值,关系运算符、成员测试运算符、同一性测试运算符组成的表达式的值一般为True或False
空类型 NoneType None 空值
异常 BaseException Exception, ValueError… Python内置大量异常类,分别对应不同类型的异常
文件 f = open(‘data.dat’, ‘rb’) open是Python内置函数,使用指定的模式打开文件,返回文件对象
其它可迭代对象 生成器对象、
range对象、
zip对象、
enumerate对象、
map对象、
filter对象等
range(1, 3), … 具有惰性求值的特点,除range对象之外,其他对象中的元素只能看一次
编程单元 函数、类、模块(类型为module) 类和函数都属于可调用对象,模块用来集中存放函数、类、常量或其他对象

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
a = 3 + 4j
b = 5 + 6j
c = a + b
print(c) # (8+10j)
print(c.real, c.imag, c.conjugate()) # 8.0 10.0 (8-10j)

# Python 3.6.x开始支持在数字中间位置使用单个下划线作为分隔来提高
# 数字的可读性,类似于数学上使用逗号作为千位分隔符。

print(1_000_000) # 1000000
print(1_2_3_4) # 1234
print(1_2 + 3_4j) # (12+34j)
print(1_2.3_45) # 12.345

Python运算符

取反

1
2
3
4
5
a = 2
print(~a) # -3
# 2的二进制表示为 010,其中最高位为符号位
# 取反得 101,将其取反+1可以得到它对应的正数 011,即 +3
# 所以补码 101 为 -3,即上述 print 结果。

集合运算

集合的交集、并集、对称差集等运算借助于位运算符来实现,而差集则使用减号运算符实现(并集运算符不是加号)。

1
2
3
4
5
6
7
8
>>> {1, 2, 3} | {3, 4, 5}         # 并集,自动去除重复元素 a.union(b)
{1, 2, 3, 4, 5}
>>> {1, 2, 3} & {3, 4, 5} # 交集 a.intersection(b)
{3}
>>> {1, 2, 3} ^ {3, 4, 5} # 对称差集 a.symmetric_difference(b)
{1, 2, 4, 5}
>>> {1, 2, 3} - {3, 4, 5} # 差集 a.difference(b)
{1, 2}

矩阵相乘运算符@

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
class Test:
def __matmul__(self, value):
print('__matmul__', value)
return self

def __rmatmul__(self, value):
print('__rmatmul__', value)
return self

def __imatmul__(self, value):
print('__imatmul__', value)
return self


a = Test()
b = Test()
c = a @ b # __matmul__ <__main__.Test object at 0x000001E098F8A2E0>
d = 1 @ a # __rmatmul__ 1
e = a @ 2 # __matmul__ 2
c @= c # __imatmul__ <__main__.Test object at 0x000001E098F2A550>
d @= 3 # __imatmul__ 3

import numpy as np
x = np.ones(3)
y = np.eye(3) * 3
y[0, 2] = 5
y[2, 0] = 3
print(x @ y) # [6. 3. 8.]

内置函数

iter() 返回指定对象的可迭代对象

locals() 返回包含当前作用域内局部变量及其值的字典

next(iterator[, default]) 返回可迭代对象中的下一个元素,允许指定迭代结束之后继续迭代时的返回值

reduce(func, sequence[, initial]) 将双参数的函数 $func$ 以迭代的方式从左到右依次应用至序列 $sequence$ 中每个元素,最终返回单个值作为结果。在Python 2.x中该函数为内置函数,在Python 3.x中需要从functools中导入reduce函数再使用

repr(obj) 返回对象 $obj$ 的规范化字符串表示形式,对于大多数对象有 $eval(repr(obj)) == obj$。

str(obj) 把对象 $obj$ 直接转换为字符串

reversed(seq) 返回 $seq$ 的反向迭代器

sorted(iterable, key=None, reverse=False) 返回排序后的列表,其中 $iterable$ 表示要排序的序列或迭代对象,$key$ 用来指定排序规则或依据,$reverse$ 用来指定升序或降序。该函数不改变 $iterable$ 内任何元素的顺序

zip(seq1 [, seq2 […]]) 返回 $zip$ 对象,其中元素为 $(seq1[i], seq2[i], …)$ 形式的元组,最终结果中包含的元素个数取决于所有参数序列或可迭代对象中最短的那个

sum(iterable, start=0) 求和,其中 $sum$ 为初值。

isinstance(obj, class_or_tuple) 判断 $obj$ 是否是该类或该类的子类的对象。instance(x, (A, B, ...)) 等价于 ``isinstance(x, A) or isinstance(x, B) or …`。

filter(function or None, iterable) 返回一个 $filter$ 对象,生成满足 $function(item) == True$ 的元素,其中 $item$ 为 $iterable$ 中的元素。若第一参数为 $None$,则判定条件变为元素与 $True$ 等价。

enumerate(iterable, start=0) 返回一个 $enumerate$ 对象,生成一个个由 $count$ 和 $value$ 组成的元组。即该对象会返回 $(0, seq[0]), (1, seq[1]), (2, seq[2]), …$。

STL中_Rb_tree的探索

我们知道STL中我们常用的setmultisetmapmultimap都是基于红黑树。本文介绍了它们的在STL中的底层数据结构_Rb_tree的直接用法与部分函数。难点主要是_Rb_tree的各个参数的确定。

特别注意在如下代码的Selector类用于从Node中选出用于排序的key值,这个仿函数必须返回const int&而不能是int,否则less<int>::operator(const int&, const int&)会抛出segmentation fault。一开始写成int,看了很多源码才发现是这个原因,一定要注意。(Update on 2020-5-6:通过对源码阅读,终于彻底明白为何出现写int会出现问题,见文末)

接下来是样例代码,里面都有注释了。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <iomanip>

// 原则上不要直接引用这个头文件,这里只是为了测试
#include <bits/stl_tree.h>

using namespace std;

struct Node {
int first, second;
Node(int _first, int _second) : first(_first), second(_second){};

friend ostream& operator<<(ostream& outs, const Node& node) {
outs << '{' << node.first << ',' << node.second << '}';
return outs;
}
};

template <class T>
struct Selector {
// MUST return const int&, not int.
// if return int, segmentation fault will occur.
// I have spent much time because of this.
const int& operator()(const T& obj) const {
return obj.first;
}
};

int main() {
// _Rb_tree: red-black tree in STL.
using tree_type = _Rb_tree<int, Node, Selector<Node>, less<int>>;
using iterator_type = tree_type::iterator;
using result_pair_type = pair<tree_type::iterator, bool>;
tree_type tree;

// 插入元素Node(1, 2)
result_pair_type res = tree._M_insert_unique(Node(1, 2));
cout << "insert address = " << res.first._M_node << endl;
cout << "insert result = " << boolalpha << res.second << endl; // true

iterator_type it = tree.begin();
cout << "begin address = " << it._M_node << endl;

it = tree.find(1);
cout << "address = " << it._M_node << ", value = " << *it << endl;

// 再插入元素Node(1, 2)但是因为调用的是insert_unique
// 它不会添加重复值,所以插入会被拒绝
res = tree._M_insert_unique(Node(1, 2));
cout << "insert result = " << boolalpha << res.second << endl; // false

// 再插入元素Node(1, 2)但这次调用insert_equal
// multiset和multimap就是利用这个函数来插入重复值
// 也就是这个函数允许重复值,所以插入成功
tree._M_insert_equal(Node(1, 3));
cout << "size = " << tree.size() << endl; // 大小就变为2

pair<iterator_type, iterator_type> result = tree.equal_range(1);
for (iterator_type ite = result.first; ite != result.second; ++ite) {
cout << "address = " << ite._M_node << ", value = " << *ite << endl;
}

return 0;
}

程序的输出为(内存地址不定):

1
2
3
4
5
6
7
8
insert address = 0xf91be0
insert result = true
begin address = 0xf91be0
address = 0xf91be0, value = {1,2}
insert result = false
size = 2
address = 0xf91be0, value = {1,2}
address = 0xf91c10, value = {1,3}

Update on 2020-5-6

当将Selector::operator()的返回值写为int时,执行时会在main()中的it = tree.find(1)时抛出Segmentation Fault。以下给出调用过程。

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
// main.cpp, main()
int main() {
...
it = tree.find(1);
...
}

// stl_tree.h, std::_Rb_tree::find(const _Key&)
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
typename _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
find(const _Key& __k) {
iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
return (__j == end() || _M_impl._M_key_compare(__k,
_S_key(__j._M_node))) ? end() : __j;
}

// stl_tree.h, std::_Rb_tree::_M_lower_bound(_Link_type, _Link_type, const _Key&)
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
typename _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
_M_lower_bound(_Link_type __x, _Link_type __y, const _Key& __k) {
while (__x != 0)
if (!_M_impl._M_key_compare(_S_key(__x), __k))
__y = __x, __x = _S_left(__x);
else
__x = _S_right(__x);
return iterator(__y);
}

在此处的 _M_impl._M_key_compare 也就是比较仿函数对象,在这里即less<int>对象。异常就是在这个函数里抛出的,可见问题出现在 _S_key(__x) 中。

1
2
3
4
// stl_tree.h, std::_Rb_tree::_S_key(_Const_Base_ptr)
static const _Key& _S_key(_Const_Base_ptr __x) {
return _KeyOfValue()(_S_value(__x));
}

这里的 _KeyOfValue() 就是我们给的 Selector 了。所以在这里如果将 operator() 的返回值写为 int ,则这里将其以 const _Key& 返回,就是返回局部对象的引用,后面又在 less<int>::operator() 里面使用了这个引用(这个引用实际上指向一个已过期的栈上数据: _KeyOfValue() 的返回值),这个行为导致了Segmentation Fault

解决Navicat连接MySQL不成功的问题

因为在虚拟机上使用MySQL不方便保存数据,尝试在Windows上使用Navicat连接Linux虚拟机上的MySQL。

在Navicat中输入用户名/密码后,提示

Can't connect to MySQL server on '<MySQL Server IP>' (10061 "Unknown error")

解决方法:

  1. 修改 /etc/mysql/mysql.conf.d/mysqld.cnf ,将其中的 bind-address = 127.0.0.1 注释掉,保存。
  2. 运行 service mysql restart ,重启 MySQL。

接下来再尝试在Navicat中连接MySQL,又提示

1130 - Host '<MySQL Server IP>' is not allowed to connect to this MySQL server

因为默认情况下root用户只允许本机访问,即只能使用localhost访问。所以需要将root对应的host修改大一点,这里简单起见直接修改为所有IP访问。

解决方法:

在虚拟机上以root登录MySQL。然后执行

1
2
3
mysql> use mysql;
mysql> update user set host = '%' where user = 'root';
mysql> flush privileges;

现在再用Navicat连接MySQL就可以连接成功了。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×