Python 简单入门指北(一)
Python 简单入门指北(一)
Python 是一门非常容易上手的语言,通过查阅资料和教程,也许一晚上就能写出一个简单的爬虫。但 Python 也是一门很难精通的语言,因为简洁的语法背后隐藏了许多黑科技。本文主要针对的读者是:
- 毫无 Python 经验的小白
- 有一些简单 Python 经验,但只会复制粘贴代码,不知其所以然的读者
- 觉得单独一篇文章太琐碎,质量没保证,却没空读完一本书,但又想对 Python 有全面了解的读者
当然, 用一篇文章来讲完某个语言是不可能的事情,我希望读完本文的读者可以:
- 对 Python 的整体知识结构形成初步的概念
- 了解 Python 特有的知识点,比如装饰器、上下文、生成器等等,不仅会写 Demo,还对背后的原理有一定了解
- 避免 C++/Java 等风格的 Python 代码,能够写出地道的 Python 代码
- 能够熟练的使用 Python 编写脚本实现日常的简单需求,能够维护小型 Python 项目,能够阅读较复杂的 Python 源码
如果以上介绍符合你对自己的定位,在开始阅读前,还需要明确几点:
- 本文不会只介绍用法,那样太肤浅
- 本文不会深入介绍某个知识点,比如分析源码等,那样太啰嗦,我希望做一名引路人,描述各个知识点的概貌并略作引申,为读者指出下一步的研究方向
- 代码注释非常重要,一定要看,几乎所有的代码段都可以执行,强烈建议手敲一遍!
0. 准备工作
请不要在学习 Python2 还是 Python3 之间犹豫了,除非你很明确自己只接触 Python2,否则就从 Python3 学起,新版本的语言总是意味着进步的生产力(Swift 和 Xcode 除外)。Python 2 和 3 之间语法不兼容,但这并不影响熟悉 Python3 的开发者迅速写出 Python 2 的代码,反之亦然。所以与其在反复纠结中浪费时间,不如立刻行动起来。
推荐使用 CodeRunner 来运行本文中的 demo,它比文本编辑器功能更强大,比如支持自动补全和断点调试,又比 PyCharm 轻量得多。
1. 数据结构
1.1 数组
1.1.1 列表推导
如果要对数组中的所有内容做一些修改,可以用 for 循环或者 map 函数:
- array = [, , , , , ]
- small = []
- for n in array:
- if n < :
- small.append(n * )
- print(small) # [, , ]
比较地道的 Python 写法是使用列表推导:
- array = [, , , , , ]
- small = [n * for n in array if n < ]
for in
可以写两次,类似于嵌套的 for 循环,会得到一个笛卡尔积:
- signs = ['+', '-']
- numbers = [, ]
- ascii = ['{sign}{number}'.format(sign=sign, number=number)
- for sign in signs for number in numbers]
- # 得到:['+1', '+2', '-1', '-2']
1.1.2 元组
元组可以简单的理解为不可变的数组,也就是没有 append
、del
等方法,一旦创建,就无法新增或删除元素,元素自身的值也不能改变,但元素内部的属性是否可变并不受元组的影响,这一点符合其他语言中的常识。
- t = (, [])
- t[] = # 抛出错误 TypeError: 'tuple' object does not support item assignment
- t[].append() # 正常运行,现在的 t 是 (, [])
- 除了不可变性以外,有时候元组也会被当做不具名的数据结构,这时候元素的位置就不再是可有可无的了:
- coordinate = (33.9425, -118.408056)
- # coordinate 的第一个位置用来表示精度,第二个位置表示维度
- 在解析元组数据时,可以一一对应的写上变量名:
- t = (, )
- a, b = t # a = , b =
- 有时候变量名比较长, 但我只关心其中某一个,可以这样写:
- t = (, )
- a, _ = t # a =
- 如果元组中元素特别多,即使挨个写下划线也比较累,可以用 * 来批量解包:
- t = (, , , , )
- first, *middle, last = t
- # first =
- # middle = [, , ]
- # last =
- 当然,如果元素数量较多,含义较复杂,我还是建议使用具名元组:
- import collections
- People = collections.namedtuple('People', ['name', 'age'])
- p = People('bestswifter', '')
- p.name #
- 具名元组更像是一个不能定义方法的简化版的类,能提供友好的数据展示。
- 元组的一个小技巧是可以避免用临时变量来交换两个数的值:
- a =
- b =
- a, b = b, a
- # a = , b =
1.1.3 数组切片
切片的基本格式是 array[start:end:step]
,表示对 array 在 start 到 end 之前以 step 为间隔取切片。注意这里的区间是 [start, end),也就是左闭右开。比如:
- s = 'hello'
- s[::]
- # 表示取 s 的第 、、 个字符,结果是 'hlo'
再举几个例子
- s[:] # 不写 step 默认就是 ,因此得到 'hello'
- s[:] # 不写 end 默认到结尾,因此还是得到 'ello'
- s[n:] # 获取 s 的最后 len(s) - n 个元素
- s[:] # 不写 start 默认从 开始,因此得到 'he'
- s[:n] # 获取 s 的前 n 个元素
- s[:-] # 负数表示倒过来数,因此这会刨除最后一个字符,得到 'hell'
- s[-:] # 同上,表示获取最后两个字符,得到 'lo'
- s[::-] # 获取字符串的倒序排列,相当于 reverse 函数
step 和它前面的冒号要么同时写,要么同时不写,但 start 和 end 之间的冒号不能省,否则就不是切片而是获取元素了。再次强调 array[start:end]
表示的区间是 [a, b),也许你会觉得这很难记,但同样的,这会得出以下美妙的公式:
array[:n] + array[n:] = array (0 <= n <= len(array))
用代码来表示就是:
- s = 'hello'
- s[:] + s[:] == s
- # True,因为 s[:] 是 'he',s[:] 是 'llo'
切片不仅可以用来获取数组的一部分值,修改切片也可以直接修改数组的对应部分,比如:
- a = [, , , , , ]
- a[:] = [, , ]
- # a = [, , , , , , ]
并没有人规定切片的新值必须和原来的长度一致:
- a = [, , , , , ]
- a[:] = []
- # a = [, , , , ]
- a[:] = []
- # a = [, ],相当于删除了中间的三个数字
但切片的新值必须也是可迭代的对象,比如这样写是不合法的:
- a = [, , , , , ]
- a[:] =
- # TypeError: can only assign an iterable
- 1.1.4 循环与遍历
一般来说,在 Python 中我们不会写出 for (int i = 0; i < len(array); ++i)
这种风格的代码,而是使用 for in
这种语法:
- for i in [, , ]:
- print(i)
虽然大家都知道 for in
语法,但它的某些灵活用法或许就不是那么众所周知了。
- 有时候,我们会在 if 语句中对某个变量的值做多次判断,只要满足一个条件即可:
- name = 'bs'
- if name == 'hello' or name == 'hi' or name == 'bs' or name == 'admin':
- print('Valid')
- 这种情况推荐用 in 来代替:
- name = 'bs'
- if name in ('hello', 'hi', 'bs', 'admin'):
- print('Valid')
- 有时候,如果我们想要把某件事重复固定的次数,用 for in 会显得有些啰嗦,这时候可以借助 range 类型:
- for i in range():
- print('Hi') # 打印五次 'Hi'
- range 的语法和切片类似,比如我们需要访问数组所有奇数下标的元素,可以这么写:
- a = [, , , , ]
- for i in range(, len(a), ):
- print(a[i])
- 在这种写法中,我们不仅能获得元素,还能知道元素的下标,这与使用 enumerate(iterable [, start ]) 函数类似:
- a = [, , , , ]
- for i, n in enumerate(a):
- print(i, n)
1.1.5 魔术方法
也许你已经注意到了,数组和字符串都支持切片,而且语法高度统一。这在某些强类型语言(比如我经常接触的 Objective-C 和 Java)中是不可能的,事实上,Python 能够支持这样统一的语法,并非巧合,而是因为所有用中括号进行下标访问的操作,其实都是调用这个类的 __getitem__
方法。
比如我们完全可以让自己的类也支持通过下标访问:
- class Book:
- def __init__(self):
- self.chapters = [, , ]
- def __getitem__(self, n):
- return self.chapters[n]
- b = Book()
- print(b[]) # 结果是
需要注意的是,这段代码几乎不会出问题(除非数组越界),这是因为我们直接把下标传到了内部的 self.chapters
数组上。但如果要自己处理下标,需要牢记它不一定是数字,也可以是切片,因此更完整的逻辑应该是:
- def __getitem__(self, n):
- if isinstance(n, int): # n是索引
- # 处理索引
- if isinstance(n, slice): # n是切片
- # 通过 n.start,n.stop 和 n.step 来处理切片
与静态语言不同的是,任何实现了 __getitem__
都支持通过下标访问,而不用声明为实现了某个协议,这种特性也被称为 “鸭子类型”。鸭子类型并不要求某个类 是什么,仅仅要求这个类 能做什么。
顺便说一句,实现了 __getitem__
方法的类都是可迭代的,比如:
- b = Book()
- for c in b:
- print(c)
后续的章节还会介绍更多 Python 中的魔术方法,这种方法的名称前后都有两个下划线,如果读作 “下划线-下划线-getitem” 会比较拗口,因此可以读作 “dunder-getitem” 或者 “双下-getitem”,类似的,我想每个人都能猜到 __setitem__
的作用和用法。
1.2 字典
1.2.1 初始化字典
- 最简单的创建一个字典的方式就是直接写字面量:
- {'a': , 'b': , 'c': , 'd': , 'e': }
- 字典字面量由大括号包住(注意区别于数组的中括号),键值对之间由逗号分割,每个键值对内部用冒号分割键和值。
- 如果数组的每个元素都是二元的元组,这个数组可以直接转成字典:
- dict([('a', ), ('b', ), ('c', ), ('d', ), ('e', )])
- 就像数组可以推导一样,字典也可以推导:
- a = [('a', ), ('b', ), ('c', ), ('d', ), ('e', )]
- d = {letter: number for letter, number in a} # 这里用到了元组拆包
- 只要记得外面还是大括号就行了。
- 两个独立的数组可以被压缩成一个字典:
- numbers = [, , , , ]
- letters = ['a', 'b', 'c', 'd', 'e']
- dict(zip(letters, numbers))
- 正如 zip 的意思所表示的,超出长处的那部分数组会被抛弃。
- 1.2. 查询字典
- 最简单方法是直接写键名,但如果键名不存在会抛出 KeyError:
- d = {'a': }
- d['a'] # 值是
- d['b'] # KeyError: 'b'
- 可以用 if key in dict 的判断来检查键是否存在,甚至可以先 try 再 catch KeyError,但更加优雅简洁一些的写法是用
get(k, default) 方法来提供默认值:- d = {'a': }
- d.get('a', ) # 得到
- d.get('b', ) # 得到
- 不过有时候,我们可能不仅仅要读出默认属性,更希望能把这个默认属性能写入到字典中,比如:
- d = {}
- # 我们想对字典中某个 Value 做操作,如果 Key 不存在,就先写入一个空值
- if 'list' not in d:
- d['list'] = []
- d['list'].append()
- 这种情况下,seddefault(key, default) 函数或许更合适:
- d.setdefault('key', []).append()
- 这个函数虽然名为 set,但作用其实是查找,仅仅在查找不到时才会把默认值写入字典。
1.2.3 遍历字典
- 直接遍历字典实际上是遍历了字典的键,因此也可以通过键获取值:
- d = {'a': , 'b': , 'c': , 'd': , 'e': }
- for i in d:
- print(i, d[i])
- #b
- #a
- #e
- #d
- #c
- 我们也可以用字典的 keys() 或者 values() 方法显式的获取键和值。字典还有一个 items() 方法,它返回一个数组,
每个元素都是由键和值组成的二元元组:- d = {'a': , 'b': , 'c': , 'd': , 'e': }
- for (k, v) in d.items():
- print(k, v)
- #e
- #d
- #a
- #c
- #b
可见 items()
方法和字典的构造方法互为逆操作,因为这个公式总是成立的:
dict(d.items()) == d
1.2.4 字典的魔术方法
在 1.1.4 节中介绍过,通过下标访问最终都会由 __getitem__
这个魔术方法处理,因此字典的 d[key]
这种写法也不例外, 如果键不存在,则会走到 __missing__
方法,再给一次挽救的机会。比如我们可以实现一个字典, 自动忽略键的大小写:
- class MyDict(dict):
- def __missing__(self, key):
- if key.islower():
- raise KeyError(key)
- else:
- return self[key.lower()]
- d = MyDict({'a': })
- d['A'] # 返回
- 'A' in d # False
这个字典比较简陋,比如 key 可能不是字符串,不过我没有处理太多情况,因为它主要是用来演示 __missing__
的用法,如果想要最后一行的 in
语法正确工作,需要重写 __contains__
这个魔术方法,过程类似,就不赘述了。
虽然通过自定义的函数也能实现相似的效果,不过这个自定义字典对用户更加透明,如果不在文档中说明,调用方很难察觉到字典的内部逻辑被修改了。 Python 有很多强大的功能,可以具备这种内部进行修改,但是对外保持透明的能力。这可能是我们第一次体会到,后续还会不断的经历。
1.2.5 集合
- 集合更像是不会有重复元素的数组,但它的本质是以元素的哈希值作为 Key,从而实现去重的逻辑。因此,集合也可以推导,不过得用字典的语法:
- a = [,,,,,,,,]
- d = {i for i in a if i < }
- # d = {, , , },注意这里的大括号
- 回忆一下,二进制逻辑运算一共有三个运算符,按位或 |,按位与 & 和异或 ^,这三个运算符也可以用在集合之间,而且含义变化不大。比如:
- a = {, , }
- b = {, , }
- c = a | b
- # c = {, , , , }
- 这里的 | 运算表示交集,也就是 c 中的任意元素,要么在 a,要么在 b 集合中。类似的,按位与 & 运算求的就是交集:
- a = {, , }
- b = {, , }
- c = a & b
- # c = {}
- 而异或则表示那些只在 a 不在 b 或者只在 b 不在 a 的元素。或者换个说法,表示那些在集合 a 和 b 中出现了且仅出现了一次的元素:
- a = {, , }
- b = {, , }
- c = a ^ b
- # c = {, , , }
- 还有一个差集运算 -,表示在集合 a 中但不在集合 b 中的元素:
- a = {, , }
- b = {, , }
- c = a - b
- # c = {, }
回忆一下韦恩图,就会得到以下公式(虽然并没有什么卵用):
A | B = (A ^ B) | (A & B)
A ^ B = (A - B) | (B - A)
1.3 字符串
1.3.1 字符串编码
用 Python 写过爬虫的人都应该感受过被字符串编码支配的恐惧。简单来说,编码指的是将可读的字符串转换成不太可读的数字,用来存储或者传输。解码则指的是将数字还原成字符串的过程。常见的编码有 ASCII、GBK 等。
ASCII 编码是一个相当小的字符集合,只有一百多个常用的字符,因此只用一个字节(8 位)就能表示,为了存储本国语言,各个国家都开发出了自己的编码,比如中文的 GBK。这就带来了一个问题,如果我想要在一篇文章中同时写中文和日文,就无法实现了,除非能对每个字符指定编码,这个成本高到无法接受。
Unicode 则是一个最全的编码方式,每个 Unicode 字符占据 6 个字节,可以表示出 2 ^ 48 种字符。但随之而来的是 Unicode 编码后的内容不适合存储和发送,因此诞生了基于 Unicode 的再次编码,目的是为了更高效的存储。
更详细的概念分析和配图说明可以参考我的这篇文章:字符串编码入门科普,这里我们主要聊聊 Python 对字符串编码的处理。
首先,编码的函数是 encode
,它是字符串的方法:
- s = 'hello'
- s.encode() # 得到 b'hello'
- s.encode('utf16') # 得到 b'\xff\xfeh\x00e\x00l\x00l\x00o\x00'
encode
函数有两个参数,第一个参数不写表示使用默认的 utf8
编码,理论上会输出二进制格式的编码结果,但在终端打印时,被自动还原回字符串了。如果用 utf16
进行编码,则会看到编码以后的二进制结果。
前面说过,编码是字符转到二进制的转化过程,有时候在某个编码规范中,并没有指定某个字符是如何编码的,也就是找不到对应的数字,这时候编码就会报错:
- city = 'São Paulo'
- b_city = city.encode('cp437')
- # UnicodeEncodeError: 'charmap' codec can't encode character '\xe3'
- in position : character maps to <undefined>
- 此时需要用到 encode 函数的第二个参数,用来指定遇到错误时的行为。它的值可以是 'ignore',表示忽略这个不能编码的字符,
也可以是 'replace',表示用默认字符代替:- b_city = city.encode('cp437', errors='ignore')
- # b'So Paulo'
- b_city = city.encode('cp437', errors='replace')
- # b'S?o Paulo'
decode
完全是 encode
的逆操作,只有二进制类型才有这个函数。它的两个参数含义和 encode
函数完全一致,就不再介绍了。
从理论上来说,仅从编码后的内容上来看,是无法确定编码方式的,也无法解码出原来的字符。但不同的编码有各自的特点,虽然无法完全倒推,但可以从概率上来猜测,如果发现某个二进制内容,有 99% 的可能性是 utf8
编码生成的,我们就可以用 utf8
进行解码。Python 提供了一个强大的工具包 Chardet
来完成这一任务:
- octets = b'Montr\xe9al'
- chardet.detect(octets)
- # {'encoding': 'ISO-8859-1', 'confidence': 0.73, 'language': ''}
- octets.decode('ISO-8859-1')
- # Montréal
返回结果中包含了猜测的编码方式,以及可信度。可信度越高,说明是这种编码方式的可能性越大。
有时候,我们拿到的是二进制的字符串字面量,比如 68 65 6c 6c 6f
,前文说过只有二进制类型才有 decode
函数,所以需要通过二进制的字面量生成二进制变量:
- s = '68 65 6c 6c 6f'
- b = bytearray.fromhex(s)
- b.decode() # hello
1.3.2 字符串的常用方法
字符串的 split(sep, maxsplit)
方法可以以指定的分隔符进行分割,有点类似于 Shell 中的 awk -F ' '
',第一个 sep
参数表示分隔符,不填则为空格:
- s = 'a b c d e'
- a = s.split()
- # a = ['a', 'b', 'c', 'd', 'e']
- 第二个参数 maxsplit 表示最多分割多少次,因此返回数组的长度是 maxsplit + 。举个例子说明下:
- s = 'a;b;c;d;e'
- a = s.split(';')
- # a = ['a', 'b', 'c', 'd', 'e']
- b = s.split(';', )
- # b = ['a', 'b', 'c;d;e']
- 如果想批量替换,则可以用 replace(old, new[, count]) 方法,由中括号括起来的参数表示选填。
- old = 'a;b;c;d;e'
- new = old.replace(';', ' ', )
- # new = 'a b c d;e'
- strip[chars] 用于移除指定的字符们:
- old = "*****!!!Hello!!!*****"
- new = old.strip('*') # 得到 '!!!Hello!!!'
- new = old.strip('*!') # 得到 'Hello'
- 如果不传参数,则默认移除空格。其实 strip 等价于分别执行 lstrip() 和 rstrip(),即分别从左侧和右侧进行移除。
比如 lstrip() 表示从左侧第一个字符开始,移除空格,直到第一个非空格字符为止,所以字符串中间的空格,无论是 lstrip
还是 strip() 都是无法移除的。- old = ' Hello world '
- new = old.strip() # 得到 'Hello wrold'
- new = old.lstrip() # 得到 'Hello world '
- 最后一个常用方法是 join,其实这个可以理解为字符串的构造方法,它可以把数组转换成字符串:
- array = 'a b c d e'.split() # 之前说过,结果是 ['a', 'b', 'c', 'd', 'e']
- s = ';'.join(array) # 以分号为连接符,把数组中的元素连接起来
- # s = 'a;b;c;d;e'
所以 join
可以理解为 split
的逆操作,这个公式始终是成立的:
c.join(string.split(c)) = string
上面这些字符串处理的函数,大多返回的还是字符串,因此可以链式调用,避免使用临时变量和多行代码,但也要避免过长(超过 3 个)的链式调用,以免影响可读性。
1.3.3 字符串格式化
最初级的字符串格式化方法是使用 +
来拼接:
- class Person:
- def __init__(self):
- self.name = 'bestswifter'
- self.age =
- self.sex = 'm'
- p = Person()
- print('Name: ' + p.name + ', Age: ' + str(p.age) + ', Sex: ' + p.sex)
- # 输出:Name: bestswifter, Age: , Sex: m
这里必须要把 int
类型的年龄转成字符串以后才能进行拼接,这是因为 Python 是强类型语言,不支持类型的隐式转换。
这种做法的缺点在于如果输出结构比较复杂,极容易出现引号匹配错误的问题,可读性非常低。
Python 2 中的做法是使用占位符,类似于 C 语言中 printf
:
- content = 'Name: %s, Age: %i, Sex: %c' % (p.name, p.age, p.sex)
- print(content)
从结构上看,要比上一种写法清楚得多, 但每个变量都需要指定类型,这和 Python 的简洁不符。实际上每个对象都可以通过 str()
函数转换成字符串,这个函数的背后是 __str__
魔术方法。
Python 3 中的写法是使用 format
函数,比如我们来实现一下 __str__
方法:
- class Person:
- def __init__(self):
- self.name = 'bestswifter'
- self.age =
- self.sex = 'm'
- def __str__(self):
- return 'Name: {user.name}, Age: {user.age}, Sex: {user.sex}'
- .format(user=self)
- p = Person()
- print(p)
- # 输出:Name: bestswifter, Age: , Sex: m
- 除了把对象传给 format 函数并在字符串中展开以外, 也可以传入多个参数,并且通过下标访问他们:
- print('{0}, {1}, {0}'.format(, ))
- # 输出:, , ,这里的 {} 表示第二个参数
1.3.4 HereDoc
Heredoc 不是 Python 特有的概念, 命令行和各种脚本中都会见到,它表示一种所见即所得的文本。
- 假设我们在写一个 HTML 的模板,绝大多数字符串都是常量,只有有限的几个地方会用变量去替换,那这个字符串该如何表示呢?
一种写法是直接用单引号去定义:- s = '<HTML><HEAD><TITLE>\nFriends CGI Demo</TITLE></HEAD>\n<BODY><H3>ERROR
- </H3>\n<B>%s</B><P>\n<FORM><INPUT TYPE=button VALUE=Back\nONCLICK=\'window
- .history
- .back()\'></FORM>\n</BODY></HTML>'
- 这段代码是自动生成的还好,如果是手动维护的,那么可读性就非常差,因为换行符和转义后的引号增加了理解的难度。
如果用 heredoc 来写,就非常简单了:- s = '''<HTML><HEAD><TITLE>
- Friends CGI Demo</TITLE></HEAD>
- <BODY><H3>ERROR</H3>
- <B>%s</B><P>
- <FORM><INPUT TYPE=button VALUE=Back
- ONCLICK='window.history.back()'></FORM>
- </BODY></HTML>
- '''
Heredoc 主要是用来书写大段的字符串常量,比如 HTML 模板,SQL语句等等。
Python 简单入门指北(一)的更多相关文章
- Python 简单入门指北(二)
Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...
- Celery入门指北
Celery入门指北 其实本文就是我看完Celery的官方文档指南的读书笔记.然后由于我的懒,只看完了那些入门指南,原文地址:First Steps with Celery,Next Steps,Us ...
- 关于supervisor的入门指北
关于supervisor的入门指北 在目前这个时间点(2017/07/25),supervisor还是仅支持python2,所以我们要用版本管理pyenv来隔离环境. pyenv 根据官方文档的讲解, ...
- Angular 从入坑到挖坑 - Router 路由使用入门指北
一.Overview Angular 入坑记录的笔记第五篇,因为一直在加班的缘故拖了有一个多月,主要是介绍在 Angular 中如何配置路由,完成重定向以及参数传递.至于路由守卫.路由懒加载等&quo ...
- python简单入门
一. 初识python. 1. 认识计算机 CPU(大脑) 3GHZ + 内存(DDR4) + 主板 + 电源(心脏)+ 显示器 + 键盘 +鼠标+ 显卡 + 硬盘 80MB/s 操作系统 windo ...
- 写给前端的Python依赖管理指北
概述 在Python的项目中,我们可以通过pip来安装依赖包,但是不像npm install,pip默认安装的依赖包会挂在全局上,不利于项目工程协作. 这时候需要一款类似npm的工具记录我们的项目依赖 ...
- Electron入门指北
最近几年最火的桌面化技术,无疑是Qt+和Electron. 两者都有跨平台桌面化技术,并不局限于Windows系统.前者因嵌入式而诞生,在演变过程中,逐步完善了生态以及工具链.后者则是依托于Node. ...
- SourceGenerator入门指北
SourceGenerator介绍 SourceGenerator于2020年4月29日在微软的.net blog首次介绍,大概说的是开发者编可以写分析器,在项目代码编译时,分析器分析项目既有的静态代 ...
- Python简单入门心得(一)
很久之前就对Python感兴趣了,但是一直没时间学习,最近两天还有点时间,于是网上看了下视频,Python不愧是强类型的编程语言,对每一行的缩进的都有很严格的要求,比如一个判断,如果条件语句else不 ...
随机推荐
- java多线程之守护线程(Daemon)
https://blog.csdn.net/u010739551/article/details/51065923/
- ns2.34 移植MFLOOD协议时出现的问题
安全按照<NS网络模拟核协议仿真>第11章的步骤进行修改,但是make的时候出现了一下错误: make[1]: 正在进入目录 `/home/wang/ns/ns-allinone-2.34 ...
- equals()与hashCode()
两个都可以用来判断两个对象是否相同一致. hashCode相同的不一定是同一个对象:hashCode不同的一定不是相同对象 equals相同的一定是相同对象,是绝对可靠的 既然equals这么可靠,那 ...
- Java 接口 Closeable
该接口位于java.io包下,声明如下:public interface Closeable extends AutoCloseable.关闭流并释放与该流关联的所有系统资源.如果已经关闭该流,则调用 ...
- java第十三周测试记录
今天课上遇到了问题,在我的上一篇随笔,这个阻碍了我很长时间,而且上一次也是这个问题,真的吃一堑不长一智,这次我应该就记住了,嗯. 设计思路: 俩个库: 1.一个库存商品,商品的基本属性和商品的数量(数 ...
- 初识hibernate——环境搭建
一 配置过程 1. 创建一个项目 2. 导包 required里的包 optional里的c3p0连接池的三个包 数据库驱动包 Junit 3.创建Hibernate的配置文件(hiberna ...
- Ubuntu1404 开启定时任务 crontab
crontab -e 这个 我使用vim编辑,所以选择3,进入到 写了两条,的确隔了一分钟在test.txt文件夹里面会多加一条Good morning进去.而且也会执行dingshi.sh这个she ...
- Linux之源码安装nginx,并按照作业一描述的那样去测试使用
作业五:源码安装nginx,并按照作业一描述的那样去测试使用 [root@localhost nginx]# yum install gcc-* glibc-* openssl openssl-dev ...
- Vue(二十三)vuex + axios + 缓存 运用 (以登陆功能为例)
(一)axios 封装 (1)axios拦截器 可以在axios中加入加载的代码... (2)封装请求 后期每个请求接口都可以写在这个里面... (二)vuex user.js import { lo ...
- 首席技术官应该考虑的网络安全问题 IT大咖说 - 大咖干货,不再错过
首席技术官应该考虑的网络安全问题 IT大咖说 - 大咖干货,不再错过 http://www.itdks.com/dakalive/detail/5523