def drop_nonattrs(d, type): if not isinstance(d, dict): return d attrs_attrs = getattr(type, '__attrs_attrs__', None) if attrs_attrs is None: raise ValueError(f'type {type} isnotanattrsclass') attrs: Set[str] = {attr.name for attr in attrs_attrs} return {key: val for key, valin d.items()if key in attrs}
from typing import Set from attr import attrs, attrib import cattr
@attrs classPoint(object): x = attrib(type=int, default=0) y = attrib(type=int, default=0)
defdrop_nonattrs(d, type): ifnotisinstance(d, dict): returnd attrs_attrs = getattr(type, '__attrs_attrs__', None) ifattrs_attrsisNone: raiseValueError(f'type {type} isnot an attrs class') attrs: Set[str] = {attr.name for attr in attrs_attrs} return {key: val for key, val in d.items() if key in attrs}
import csv with open("employee.csv", mode="r") as csv_file: csv_reader = csv.DictReader(csv_file) line_count = 0 for row in csv_reader: if line_count == 0: print(f'Column names are {", ".join(row)}') line_count += 1 print(f'\\t{row["name"]} salary: {row["salary"]}' f'and was born in {row["birthday month"]}.') line_count += 1 print(f'Processed {line_count} lines.')
将代码分解为函数有助于使复杂的代码变的易于阅读和调试。 这里的代码在 with 语句中执行多项操作。为了提高可读性,您可以将带有 process salary 的代码从 CSV 文件中提取到另一个函数中,以降低出错的可能性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import csv with open("employee.csv", mode="r") as csv_file: csv_reader = csv.DictReader(csv_file) line_count = 0 process_salary(csv_reader)
defprocess_salary(csv_reader): """Process salary of user from csv file.""" for row in csv_reader: if line_count == 0: print(f'Column names are {", ".join(row)}') line_count += 1 print(f'\\t{row["name"]} salary: {row["salary"]}' f'and was born in {row["birthday month"]}.') line_count += 1 print(f'Processed {line_count} lines.')
代码是不是变得容易理解了不少呢。 在这里,创建了一个帮助函数,而不是在 with 语句中编写所有内容。这使读者清楚地了解了函数的实际作用。如果想处理一个特定的异常或者想从 CSV 文件中读取更多的数据,可以进一步分解这个函数,以遵循单一职责原则,一个函数一做一件事。这个很重要
defcall_weather_api(url, location): """Get the weather of specific location. Calling weather api to check for weather by using weather api and location. Make sure you provide city name only, country and county names won't be accepted and will throw exception if not found the city name. :param url:URL of the api to get weather. :type url: str :param location:Location of the city to get the weather. :type location: str :return: Give the weather information of given location. :rtype: str"""
defcall_weather_api(url: str, location: str) -> str: """Get the weather of specific location. Calling weather api to check for weather by using weather api and location. Make sure you provide city name only, country and county names won't be accepted and will throw exception if not found the city name. """
"""This module contains all of the network related requests. This module will checkforall the exceptionswhile making the network calls andraiseexceptionsforanyunknown exception. Make sure that when you use this module, you handle these exceptionsinclient code as: NetworkError exceptionfor network calls. NetworkNotFound exceptionif network not found. """ import urllib3 import json
在为模块编写文档字符串时,应考虑执行以下操作:
对当前模块写一个简要的说明
如果想指定某些对读者有用的模块,如上面的代码,还可以添加异常信息,但是注意不要太详细。
1 2
NetworkError exception for network calls. NetworkNotFound exception if network not found.
classStudent: """Student class information. This class handle actions performed by a student. This class provides information about student full name, age, roll-number and other information. Usage: import student student = student.Student() student.get_name() >>> 678998 """ def__init__(self): pass
这个类 docstring 是多行的; 我们写了很多关于 Student 类的用法以及如何使用它。
函数的 docstring
函数文档字符串可以写在函数之后,也可以写在函数的顶部。
1 2 3 4 5 6 7 8 9 10 11 12
def is_prime_number(number): """Checkfor prime number.
Check the given numberis prime number ornotby checking against all the numbers less the square root of given number.
:param number:Given numbertocheckfor prime :typenumber: int :return: Trueifnumberis prime otherwise False. :rtype: boolean """
如果我们使用类型注解对其进一步优化。
1 2 3 4 5 6 7
defis_prime_number(number: int)->bool: """Check for prime number. Check the given number is prime number or not by checking against all the numbers less the square root of given number. """
import pathlib now_path = pathlib.Path.cwd() from datetime import datetime time, file_path = max((f.stat().st_mtime, f) for f in now_path.iterdir()) print(datetime.fromtimestamp(time), file_path)
甚至可以使用类似的表达式获取上次修改的文件内容
1 2 3 4 5
import pathlib from datetime import datetime now_path =pathlib.Path.cwd() result = max((f.stat().st_mtime, f) for f in now_path.iterdir())[1] print(result.read_text())
.stat().st_mtime 会返回文件的时间戳,可以使用 datetime 或者 time 模块对时间格式进行进一步转换。
入职微软之后,这边大多数是使用 Windows 进行开发的,比如我的台式机是 Windows 的,还有一部分服务器是 Windows 的,当然 Linux 是也非常多。 很多情况下我是使用自己的 Mac 笔记本来远程连接我的 Windows 机器来开发的。比如如果我在工位上,我会用我的 Mac 连接两块显示屏,然后一种一块用来远程桌面连接我的 Windows 开发机,这样另外一块屏幕和 Mac 自带的屏幕就用来看文档或者使用 Teams 通讯等等。如果我回家了,我家里也是有两块屏,开上 VPN,照样用一块屏使用远程桌面,另外一块屏幕和 Mac 自带屏幕就可以做其他事情了。 这样就解决了一个问题:我的 Windows 基本上都是仅用作开发的,一块屏幕就开着一个 Visual Studio,其他的操作都会在 Mac 进行,比如查文档,发消息等等。这样我下班之后照样使用远程连接的方式来操作,和在公司就是一样的。这样就避免了一些软件的来回登录,比如如果我上班只用公司机器,下班了之后换了 Mac 还得切 Teams、切微信、切浏览器等等,还是很麻烦的,而且上班期间 Mac 就闲置了也不好。所以我就采取了这样的开发方案。
需求分析
有了这个情景,就引入了一个问题。开了一个远程桌面之后,我几乎一个屏幕都是被 Visual Studio 占据的,而远程桌面貌似只能开一个屏幕?如果我要再开一个终端窗口的话,那可能屏幕就不太够用了,或者它就得覆盖我的全屏 Visual Stuido。 另外我平时 Mac 终端软件都是使用 SSH 的,基本都是用来连 Linux 的,Windows 一般都是开远程桌面。但命令行这个情形的确让我头疼,让我感到不够爽,因为毕竟远程桌面之后,Windows 里面的操作都得挤在一个桌面里面操作了。当然可能能设置多个桌面,如果可以的话,麻烦大家告知一下谢谢。 所以解决的痛点在于:我要把一些操作尽量从 Windows 里面分离出来,例如终端软件,我能否在远程桌面外面操作,能否使用 SSH 来控制我的 Windows 机器。 好,有需求才有动力,说干就干。
如果是放开的,那么结果会提示 OpenSSH-Server-In-TCP这个状态是 enabled。 好了,完成如上操作之后我们就可以使用 SSH 来连接我们的 Windows 服务器了。
连接
连接非常简单了,用户名密码就是 Windows 的用户名和密码,使用 IP 地址链接即可。 比如我的 Windows 开发机的局域网 IP 为:10.172.134.88,那么就可以使用如下命令完成链接:
1
ssh user@10.172.134.88
然后输入密码,就连接成功了,和 Linux 的是一样的。 另外我自己现在 Mac 常用的 SSH 客户端工具有 Termius,可以多终端同步使用,非常方便,这里我只需要添加我的 Windows 机器就好了,如图所示: OK,以后就可以非常轻松地用 SSH 连接我的 Windows 服务器了,爽歪歪,上面的需求也成功解决。 以上便是使用 SSH 来连接 Windows 服务器的方法,如果大家有需求可以试试。
(function (a) { a.fn.typewriter = function () { this.each(function () { var d = a(this), c = d.html(), b = 0; d.html(""); var e = setInterval(function () { var f = c.substr(b, 1); if (f == "<") { b = c.indexOf(">", b) + 1 } else { b++ } d.html(c.substring(0, b) + (b & 1 ? "_" : "")); if (b >= c.length) { clearInterval(e) } }, 75) }); return this } })(jQuery);
这里可以看到,首先获取了页面代码区域的内容,然后通过 DOM 操作将代码先清空,然后利用 setInterval 方法设置一个定时器,定时间隔 75 毫秒,也就是说 75 毫秒循环调用一次。每调用一次,就会从原来的字符上多取一个字符,然后尾部拼接一个下划线就好了。
# You are the greatest love of my life. whileTrue: ifu.with(i): you = everything else: everything = u
这个代码的含义叫做“无论天涯海角,你都是我的一切。“,一个 while True 循环代表了永久。 这些代码其实都是在 HTML 代码中预定义好的,其中注释需要用 span 标签配以 comments 的 class 来修饰,缩进需要用 span 标签配以 placeholder 的 class 来修饰,例如:
1 2 3 4 5 6
<spanclass="comments"># You are the greatest love of my life.</span><br/> while <spanclass="keyword">True</span>:<br/> <spanclass="placeholder"></span><spanclass="keyword">if</span> u.with(i):<br/> <spanclass="placeholder"></span><spanclass="placeholder"></span>you = everything<br/> <spanclass="placeholder"></span><spanclass="keyword">else</span>:<br/> <spanclass="placeholder"></span><spanclass="placeholder"></span>everything = u<br/>
这里不同的格式用 span 的不同 class 来标识,空格缩进一个 placeholder 是两个空格,comments 代表注释格式,关键词使用 keyword 来标识。如果你需要自定义自己的内容,通过控制这些内容穿插写入就好了。
functiontimeElapse(c) { var e = Date(); var f = (Date.parse(e) - Date.parse(c)) / 1000; var g = Math.floor(f / (3600 * 24)); f = f % (3600 * 24); var b = Math.floor(f / 3600); if (b < 10) { b = "0" + b } f = f % 3600; var d = Math.floor(f / 60); if (d < 10) { d = "0" + d } f = f % 60; if (f < 10) { f = "0" + f } }
function getHeartPoint(c) { var b = c / Math.PI; var a = 19.5 * (16 * Math.pow(Math.sin(b), 3)); var d = -20 * (13 * Math.cos(b) - 5 * Math.cos(2 * b) - 2 * Math.cos(3 * b) - Math.cos(4 * b)); return newArray(offsetX + a, offsetY + d) }
作为一名程序员,能够利用好工具提高开发和工作效率是非常重要的。我个人使用的都是苹果系列产品,电脑为 MacBook Pro 15 寸,手机 iPhone 7P,另外还有一个 iPad Pro 和一副 Apple Watch。我一直觉得 Mac 是非常适合做程序开发的,它既有比较不错的页面,也有类 Unix 的操作系统,使得日常使用和程序开发都极其便利,另外由于苹果本身自有的 iCloud 机制,使用 Mac、iPhone、iPad 跨平台开发和工作也变得十分便利。
近期我又对自己的一些工具进行了整理,弃用了一些工具,新启用了一些工具。目的也只有一个,就是提高自己的工作和开发效率,让生活变得更美好。如果你也在用 Mac 开发,或者你也有使用 iPad、iPhone,下面我所总结的个人的一些工具或许能给你带来帮助。
快速导航
这是 Mac 上的一个工具,要说到提高效率,首推 Alfred,可以说是 Mac 必备软件,利用它我们可以快速地进行各种操作,大幅提高工作效率,如快速打开某个软件、快速打开某个链接、快速搜索某个文档,快速定位某个文件,快速查看本机 IP,快速定义某个色值,几乎我们能想到的都能对接实现。
其实 Mac 本身已经自带了软件搜索还有 Spotlight,但是其功能还是远远比不上 Alfred,有了它,所有的快捷操作几乎都能实现。
另外使用 Mac 和 iPhone、iPad 之间也可以相互之间复制粘贴,可以在一台 Apple 设备上拷贝文本、图像、照片和视频,然后在另一台 Apple 设备上粘贴该内容。例如,可以拷贝在 Mac 上浏览网页时发现的食谱,然后将其粘贴到附近 iPhone 上“备忘录”中的购物清单。这是在 macOS Sierra 版本之后出来的功能,若要使用需要确保 Mac 的版本是 Sierra 及以后。若要使用,几个设备必须满足“连续互通”系统要求。它们还必须在“系统偏好设置”(在 Mac 上)和“设置”(在 iOS 设备上)中打开 Wi-Fi、蓝牙和 Handoff,另外必须在所有设备上使用同一 Apple ID登录 iCloud。
言归正传,既然谈到笔记和写作。我的笔记本是 Mac,之前几乎所有的笔记,包括写书,都几乎是在 Mac 上完成的,但是确实有的时候是不方便的。比如 Mac 不在身边或者想用 iPhone 或者 iPad 来写点东西的时候,一个需要解决的问题就是云同步问题。有了云同步,我们如果在电脑上写了一部分内容,接着切换了另一台台式机,或者切换了手机的时候,照样能够接着在原来的基础上写,非常方便。
所谓爬虫的智能化解析,顾名思义就是不再需要我们针对某一些页面来专门写提取规则了,我们可以利用一些算法来计算出来页面特定元素的位置和提取路径。比如一个页面中的一篇文章,我们可以通过算法计算出来,它的标题应该是什么,正文应该是哪部分区域,发布时间是什么等等。 其实智能化解析是非常难的一项任务,比如说你给人看一个网页的一篇文章,人可以迅速找到这篇文章的标题是什么,发布时间是什么,正文是哪一块,或者哪一块是广告位,哪一块是导航栏。但给机器来识别的话,它面临的是什么?仅仅是一系列的 HTML 代码而已。那究竟机器是怎么做到智能化提取的呢?其实这里面融合了多方面的信息。
比如标题。一般它的字号是比较大的,而且长度不长,位置一般都在页面上方,而且大部分情况下它应该和 title 标签里的内容是一致的。
比如正文。它的内容一般是最多的,而且会包含多个段落 p 或者图片 img 标签,另外它的宽度一般可能会占用到页面的三分之二区域,并且密度(字数除以标签数量)会比较大。
ttf 文件: *.ttf 是字体文件格式。TTF(TrueTypeFont)是 Apple 公司和 Microsoft 公司共同推出的字体文件格式,随着 windows 的流行,已经变成最常用的一种字体文件表示方式。 @font-face 是 CSS3 中的一个模块,主要是实现将自定义的 Web 字体嵌入到指定网页中去。
简单粗暴,直接上网站部分源代码,因为这个网站应该不太希望别人来爬,所以就不上网站了。为什么这么说,因为刚开始请求的时候,老是给我返回 GO TO HELL ,哈哈。 这个网站点击鼠标右键审查元素,查看网页源代码是无法用的,但是这个好像只能防住小白啊,简单的按 F12 审查元素,CTRL+u 直接查看源代码(谷歌浏览器)。 这次的目的主要是为了获取下面的链接(重度打码)
doc = etree.HTML(con) shops = doc.xpath('//div[@id="shop-all-list"]/ul/li') for shop in shops: # 店名 name = shop.xpath('.//div[@class="tit"]/a')[0].attrib["title"] print name comment_num = 0
comment_and_price_datas = shop.xpath('.//div[@class="comment"]') for comment_and_price_data in comment_and_price_datas: _comment_data = comment_and_price_data.xpath('a[@class="review-num"]/b/node()') # 遍历每一个node,这里node的类型不同,分别有etree._ElementStringResult(字符),etree._Element(元素),etree._ElementUnicodeResult(字符) for _node in _comment_data: # 如果是字符,则直接取出 if isinstance(_node, etree._ElementStringResult): comment_num = comment_num * 10 + int(_node) else: # 如果是span类型,则要去找数据 # span class的attr span_class_attr_name = _node.attrib["class"] # 偏移量,以及所处的段 offset, position = css_and_px_dict[span_class_attr_name] index = abs(int(float(offset) )) position = abs(int(float(position))) # 判断 for key, value in svg_threshold_and_int_dict.iteritems(): if position in value: threshold = int(math.ceil(index/12)) number = int(key[threshold]) comment_num = comment_num * 10 + number print comment_num
defget_public_ip_address(): """ 获取IP和实例ID :return: {实例ID: ip} """ response = ec2.describe_instances() reservations = response.get('Reservations') instances = [i.get('Instances')[0] for i in reservations] instance_id_public_ip_address = {i.get('InstanceId'): i.get('PublicIpAddress') for i in instances} return instance_id_public_ip_address
defreboot_ec2(ip): """重启实例 :param ip: :return: """ instance_id_public_ip_address = get_public_ip_address() instance_id = instance_id_public_ip_address.get(ip) try: ec2.reboot_instances(InstanceIds=[instance_id], DryRun=True) except ClientError as e: if'DryRunOperation'notin str(e): print("You don't have permission to reboot instances.") raise
首选我们梳理一下 Linux 下的用户、用户组、文件权限等基本知识,然后后面通过一个案例来实际演示一下权限设置的一些操作。 首先 Linux 系统中,是有用户和用户组的概念的,用户就是身份的象征,我们必须以某一个用户身份来操作一个系统,实际上这就对应着我们登录系统时的账号。而用户组就是一些用户的集合,我们可以通过用户组来划分和统一管理某些用户。 比如我要在微信发一条朋友圈,我只想给我的亲人们看,难道我发的时候还要一个个去勾选所有的人?这未免太麻烦了。为了解决这问题,微信里面就有了标签的概念,我们可以提前给好友以标签的方式分类,发的时候直接勾选某个标签就好了,简单高效。实际上这就是用户组的概念,我们可以将某些人进行分组和归类,到时候只需要指定类别或组别就可以了,而不用一个个人去对号入座,从而节省了大量时间。 在 Linux 中,一个用户是可以属于多个组的,一个组也是可以包含多个用户的,下面我以一台 Ubuntu Linux 为例来演示一下相关的命令和操作。
Adding user `cqc' ... Adding new group `cqc' (1002) ... Adding new user `cqc' (1002) with group `cqc'... Creating home directory `/home/cqc' ... Copying files from `/etc/skel'... Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully Changing the user information for cqc Enter the new value, or press ENTER for the default Full Name []: Room Number []: Work Phone []: Home Phone []: Other []: Is the information correct? [Y/n]
id cqc uid=1002(cqc) gid=1002(cqc) groups=1002(cqc),27(sudo),1003(lab) id lbd uid=1004(lbd) gid=1005(lbd) groups=1005(lbd),1003(lab) id slb uid=1003(slb) gid=1004(slb) groups=1004(slb)
Welcome to fdisk (util-linux 2.27.1). Changes will remain in memory only, until you decide to write them. Be careful before using the write command.
Device does not contain a recognized partition table. Created a new DOS disklabel with disk identifier 0xc305fe54.
Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): p Partition number (1-4, default 1): First sector (2048-2145386495, default 2048): Last sector, +sectors or +size{K,M,G,T,P} (2048-2145386495, default 2145386495):
Created a new partition 1 of type 'Linux'and of size 1023 GiB.
然后使用 p 打印分区表并使用 w 将表写入磁盘,然后退出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Command (m for help): p Disk /dev/sdc: 1023 GiB, 1098437885952bytes, 2145386496 sectors Units: sectors of1 * 512 = 512bytes Sector size (logical/physical): 512bytes / 512bytes I/O size (minimum/optimal): 512bytes / 512bytes Disklabel type: dos Disk identifier: 0xc305fe54
Device Boot StartEnd Sectors SizeIdType /dev/sdc1 2048214538649521453844481023G 83 Linux
Command (m forhelp): w The partitiontable has been altered. Calling ioctl() to re-readpartition table. Syncing disks.
阻塞状态指程序未得到所需计算资源时被挂起的状态。程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的。 常见的阻塞形式有:网络 I/O 阻塞、磁盘 I/O 阻塞、用户输入阻塞等。阻塞是无处不在的,包括 CPU 切换上下文时,所有的进程都无法真正干事情,它们也会被阻塞。如果是多核 CPU 则正在执行上下文切换操作的核不可被利用。
Coroutine: <coroutine object execute at 0x10e0f7830> After calling execute Task: <Task pending coro=<execute() running at demo.py:4>> Number: 1 Task: <Task finished coro=<execute() done, defined at demo.py:4> result=1> After calling loop
Coroutine: <coroutine object execute at 0x10aa33830> After calling execute Task: <Task pending coro=<execute() running at demo.py:4>> Number: 1 Task: <Task finished coro=<execute() done, defined at demo.py:4> result=1> After calling loop
发现其效果都是一样的。
3.2 绑定回调
另外我们也可以为某个 task 绑定一个回调方法,来看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import asyncio import requests
async def request(): url = 'https://www.baidu.com' status = requests.get(url) return status
Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Cost time:15.049368143081665
Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Cost time:15.048935890197754 Task exception was never retrieved future: <Task finished coro=<request() done, defined at demo.py:7> exception=TypeError("object Response can't be used in 'await' expression",)> Traceback (most recent call last): File "demo.py", line 10, in request status = await requests.get(url) TypeError: object Response can't be used in 'await' expression
Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Cost time:15.134317874908447
Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Waiting forhttp://127.0.0.1:5000 Get response from http://127.0.0.1:5000 Result: Hello! Get response from http://127.0.0.1:5000 Result: Hello! Get response from http://127.0.0.1:5000 Result: Hello! Get response from http://127.0.0.1:5000 Result: Hello! Get response from http://127.0.0.1:5000 Result: Hello! Cost time:3.0199508666992188
一些日常工具在这里我就不一一列举了,大部分使用 Mac 的小伙伴都会安装,比如 QQ、微信、Chrome 浏览器、网易云音乐、迅雷等等,这些在 Windows 上也几乎都是必备软件,这里就不再展开说明了。
效率工具
效率工具顾名思义,可以方便和简化 Mac 的操作,提高生产工作效率的工具,下面推荐几款我比较常用的。
Alfred
首推 Alfred,可以说是 Mac 必备软件,利用它我们可以快速地进行各种操作,大幅提高工作效率,如快速打开某个软件、快速打开某个链接、快速搜索某个文档,快速定位某个文件,快速查看本机 IP,快速定义某个色值,几乎你能想到的都能对接实现。 这些快速功能是怎么实现的呢?实际上是 Alfred 对接了很多 Workflow,我们可以使用 Workflow 方便地进行功能扩展,一些比较优秀的 Workflow 已经有人专门做过整理了,可以参见:https://github.com/zenorocha/alfred-workflows。 推荐指数:★★★★★
Todoist
大家肯定也在使用各种 Todo List 的软件,这种软件其实也是五花八门,经过我本人试用,我觉得 Todoist 这款软件是最方便的。 它支持各种类型的任务定制,还可以设置分组、优先级、Deadline、执行人员、提醒、协作、效率统计等功能。另外它的各个平台支持真是异常地全啊,网页、PC、移动端就不用说了,都必须有的,另外它还有浏览器插件版、电邮版、可穿戴设备(如 Apple Watch、Google Wear)版,另外他还可以和 Mac 的日历事件进行同步,日历添加的事件也会自动添加到 Todoist 里面,非常方便,是目前我体验过的最好用的一款。 这款软件个人推荐购买专业版解锁全部功能,一个月 3 刀,但个人觉得确实非常值。 推荐指数:★★★★☆
Paste
Mac 上默认只有一个粘贴板,当我们新复制了一段文字之后,如果我们想再找寻之前复制的历史记录就找不到了,这其实是很反人类的。 好在 Paste 这款软件帮我们解决了这个问题,它可以保存我们粘贴板的历史记录,等需要粘贴某个内容的时候只需要呼出 Paste 历史粘贴板,然后选择某个特定的内容粘贴就好了,另外它还支持文本格式调整粘贴板分类和搜索,还可以支持快速便捷粘贴。有了它,妈妈再也不用担心我的粘贴板丢失了! 推荐指数:★★★★★
Synergy
工作时我会使用公司的台式机,是 Windows 系统,另外自己的个人笔记本 Mac 也会放在旁边,两台 PC 有时候会交替使用,但是我总不能配两套键盘和鼠标吧,这样就显得累赘了,而且也没那么多地方放啊。 有了 Synergy,我们可以将两台 PC 关联,实现键盘鼠标共享。我们可以使用一套键盘和鼠标来操作两台 PC,注意这是两个完全独立的 PC,各自有各自的屏幕和系统,使用 Synergy 我们可以做到一套键鼠同时控制两台电脑,鼠标可以直接从一台电脑的屏幕滑动到另一台电脑屏幕上,同时键盘、粘贴板也都是共享的。 设想这么个情景,我在我的台式机 Windows 上打开了一个页面,需要让我输入一个很长的序列号,而这个序列号又恰巧存在 Mac 上,这时如果有了 Synergy 将二者关联,我们只需要把鼠标从 Windows 的屏幕上直接滑动到 Mac 的屏幕上,选中序列号,然后键盘按下复制的快捷键,然后再把鼠标移回 Windows,粘贴即可,一气呵成。而不必再想办法发消息传输了,大大提高效率。 推荐指数:★★★★
用了 Mac,我们在使用移动硬盘的时候可能会遇到一个无法传输数据(如拷贝文件)的问题,这是因为部分移动硬盘是 NTFS 格式的,而 Mac 的磁盘不是这个格式,因此就会导致二者之间无法拷贝文件。有一个解决方法就是使用 Tuxera NTFS For Mac,有了它,我们就可以比较顺利地拷贝文件了。 另外还有其他品牌的 NTFS For Mac 软件,也可以尝试使用一下。 推荐指数:★★★★☆
VMware、Parallels Desktop
用了 Mac 之后,难免会有些情况下也还会不得不使用 Windows,毕竟很多软件可能只有 Windows 版本,但用 Mac 我就不推荐装双系统了,直接装虚拟机就好了,Mac 上虚拟机软件有两款比较好用,一个就是著名的 VMware,另一个就是 Parallels Desktop,这两款我都使用过,觉得都非常不错,现在用的是 VMware。 推荐指数:★★★★☆
CleanMyMac
很多时候用着用着磁盘就不够用了,如果你的 Mac 硬盘是 512GB 的倒还好,256GB 的你就得多注意一下了,另外 1T 定制版土豪请绕道,这款软件不适合你。 CleanMyMac 可以非常方便地帮助我们扫描缓存、大文件、废纸篓、残留项等内容,清理这些内容之后我们可以节省很多硬盘空间,另外它还支持软件卸载和残留清扫功能,可以帮我们非常干净地移除 Mac 中的软件,目前应该是出到第三版了,非常推荐。 推荐指数:★★★★☆
编辑器
既然做程序开发嘛,不配置好自己的开发环境怎么行,下面推荐一下我平常使用的开发软件。
JetBrains
我目前使用的 IDE 是 JetBrains 全家桶,目前我编写 Python 比较多,所以主要使用 PyCharm,另外写前端的时候也会使用 WebStorm,写 Java 就用 IntelliJ IDEA,C、C++ 用 CLion,PHP 的话就用 PhpStorm,Ruby 的话就用 RubyMine,其他的语言用的就少了,就没有装了。 当然有的小伙伴会说 JetBrains 系列的 IDE 需要购买啊?我只想说,国人的力量是无穷的,在网上其实可以搜到各种破解方法,如 License Server 验证,你能搜到各种五花八门的 License Server。另外 JetBrains 还有专门的 Educational Programs,可以来这里申请:https://www.jetbrains.com/education/programs/?fromMenu,学生、老师或教育工作者可以使用学校的 edu 邮箱申请免费的 License,如果你还是学生的话,那么申请是十分方便的,因为我还是个学生,我目前就在使用学生套餐,当然如果你已经工作的话也可以向正在上学的弟弟妹妹们借一下嘛。 总之我个人比较喜欢 JetBrains 全家桶,不论是页面风格还是开发习惯我都比较喜欢,推荐使用。 推荐指数:★★★★☆
对于开发者来说,这个软件几乎是 Mac 上必备的一个软件,它的官方简介就是 “The missing package manager for macOS”,算是 Mac 上的一个软件包平台,它里面包含着非常多的 Mac 开发软件包,比如 Python、PHP、Redis、MySQL、RabbitMQ、HBase 等等,几乎你能想到的开发软件都集成在里面了,堪称神器! 它的安装也非常简单,参见这里:https://brew.sh/,另外 HomeBrew 也有对应的图形界面,叫做 CakeBrew,如果不喜欢命令行操作的话可以使用 CakeBrew 来代替。 推荐指数:★★★★★
import gensim import jieba import numpy as np from scipy.linalg import norm
model_file = './word2vec/news_12g_baidubaike_20g_novel_90g_embedding_64.bin' model = gensim.models.KeyedVectors.load_word2vec_format(model_file, binary=True)
defvector_similarity(s1, s2): defsentence_vector(s): words = jieba.lcut(s) v = np.zeros(64) for word in words: v += model[word] v /= len(words) return v v1, v2 = sentence_vector(s1), sentence_vector(s2) return np.dot(v1, v2) / (norm(v1) * norm(v2))
# If these values are missing, it means we want to use the defaults. optional = { # TODO: Use custom prefixes for this settings to note that are # specific to scrapy-redis. 'queue_key': 'SCHEDULER_QUEUE_KEY', 'queue_cls': 'SCHEDULER_QUEUE_CLASS', 'dupefilter_key': 'SCHEDULER_DUPEFILTER_KEY', # We use the default setting name to keep compatibility. 'dupefilter_cls': 'DUPEFILTER_CLASS', 'serializer': 'SCHEDULER_SERIALIZER', } # 从setting中获取配置组装成dict(具体获取那些配置是optional字典中key) for name, setting_name in optional.items(): val = settings.get(setting_name) if val: kwargs[name] = val
# Support serializer as a path to a module. if isinstance(kwargs.get('serializer'), six.string_types): kwargs['serializer'] = importlib.import_module(kwargs['serializer']) # 或得一个Redis连接 server = connection.from_settings(settings) # Ensure the connection is working. server.ping()
return cls(server=server, **kwargs)
@classmethod def from_crawler(cls, crawler): instance = cls.from_settings(crawler.settings) # FIXME: for now, stats are only supported from this constructor instance.stats = crawler.stats return instance
def open(self, spider): self.spider = spider
try: # 根据self.queue_cls这个可以导入的类 实例化一个队列 self.queue = load_object(self.queue_cls)( server=self.server, spider=spider, key=self.queue_key % {'spider': spider.name}, serializer=self.serializer, ) except TypeError as e: raise ValueError("Failed to instantiate queue class '%s': %s", self.queue_cls, e)
if self.flush_on_start: self.flush() # notice if there are requests already in the queue to resume the crawl if len(self.queue): spider.log("Resuming crawl (%d requests scheduled)" % len(self.queue))
# 在Redis有序集合中数值越小优先级越高(就是会被放在顶层)所以这个位置是取得 相反数 score = -request.priority # We don't use zadd method as the order of arguments change depending on # whether the class is Redis or StrictRedis, and the option of using # kwargs only accepts strings, not bytes. # ZADD 是添加进有序集合 self.server.execute_command('ZADD', self.key, score, data)
defpop(self, timeout=0): """ Pop a request timeout not support in this queue class 有序集合不支持超时所以就木有使用timeout了 这个timeout就是挂羊头卖狗肉 """ """从有序集合中取出一个Request""" # use atomic range/remove using multi/exec """使用multi的原因是为了将获取Request和删除Request合并成一个操作(原子性的)在获取到一个元素之后 删除它,因为有序集合 不像list 有pop 这种方式啊""" pipe = self.server.pipeline() pipe.multi() # 取出 顶层第一个 # zrange :返回有序集 key 中,指定区间内的成员。0,0 就是第一个了 # zremrangebyrank:移除有序集 key 中,指定排名(rank)区间内的所有成员 0,0也就是第一个了 # 更多请参考Redis官方文档 pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0) results, count = pipe.execute() if results: return self._decode_request(results[0])
logger.info('This is a log info') logger.debug('Debugging') logger.warning('Warning exists') logger.info('Finish')
在这里我们首先引入了 logging 模块,然后进行了一下基本的配置,这里通过 basicConfig 配置了 level 信息和 format 信息,这里 level 配置为 INFO 信息,即只输出 INFO 级别的信息,另外这里指定了 format 格式的字符串,包括 asctime、name、levelname、message 四个内容,分别代表运行时间、模块名称、日志级别、日志内容,这样输出内容便是这四者组合而成的内容了,这就是 logging 的全局配置。 接下来声明了一个 Logger 对象,它就是日志输出的主类,调用对象的 info() 方法就可以输出 INFO 级别的日志信息,调用 debug() 方法就可以输出 DEBUG 级别的日志信息,非常方便。在初始化的时候我们传入了模块的名称,这里直接使用 name 来代替了,就是模块的名称,如果直接运行这个脚本的话就是 main,如果是 import 的模块的话就是被引入模块的名称,这个变量在不同的模块中的名字是不同的,所以一般使用 name 来表示就好了,再接下来输出了四条日志信息,其中有两条 INFO、一条 WARNING、一条 DEBUG 信息,我们看下输出结果:
1 2 3
2018-06-0313:42:43,526 - __main__ - INFO - This is a log info 2018-06-0313:42:43,526 - __main__ - WARNING - Warning exists 2018-06-0313:42:43,526 - __main__ - INFO - Finish
2018-06-0314:53:36,467 - __main__ - INFO - This is a log info 2018-06-0314:53:36,468 - __main__ - WARNING - Warning exists 2018-06-0314:53:36,468 - __main__ - INFO - Finish
2018-06-0315:13:44,895 - __main__ - INFO - This is a log info 2018-06-0315:13:44,947 - __main__ - WARNING - Warning exists 2018-06-0315:13:44,949 - __main__ - INFO - Finish
2018-06-0316:55:56,259 - main - INFO - Main Info 2018-06-0316:55:56,259 - main - ERROR - Main Error 2018-06-0316:55:56,259 - main.core - INFO - Core Info 2018-06-0316:55:56,259 - main.core - ERROR - Core Error
try: result = 5 / 0 except Exception as e: # bad logging.error('Error: %s', e) # good logging.error('Error', exc_info=True) # good logging.exception('Error')
memory:The memory to query; usually the output of an RNN encoder. 即解码时用到的上文信息,维度需要是 [batch_size, max_time, context_dim]。这时我们观察一下父类 _BaseAttentionMechanism 的初始化方法,实现如下:
probability_fn:A callable function which converts the score to probabilities. 计算概率时的函数,必须是一个可调用的函数,默认使用 softmax(),还可以指定 hardmax() 等函数。
score_mask_value:The mask value for score before passing into probability_fn. The default is -inf. Only used if memory_sequence_length is not None. 在使用 probability_fn 计算概率之前,对 score 预先进行 mask 使用的值,默认是负无穷。但这个只有在 memory_sequence_length 参数定义的时候有效。
dtype:The data type for the query and memory layers of the attention mechanism. 数据类型,默认是 float32。
def _bahdanau_score(processed_query, keys, normalize): dtype = processed_query.dtype # Get the number of hidden units from the trailing dimension of keys num_units = keys.shape[2].value or array_ops.shape(keys)[2] # Reshape from [batch_size, ...] to [batch_size, 1, ...] for broadcasting. processed_query = array_ops.expand_dims(processed_query, 1) v = variable_scope.get_variable( "attention_v", [num_units], dtype=dtype) if normalize: # Scalar used in weight normalization g = variable_scope.get_variable( "attention_g", dtype=dtype, initializer=math.sqrt((1. / num_units))) # Bias added prior to the nonlinearity b = variable_scope.get_variable( "attention_b", [num_units], dtype=dtype, initializer=init_ops.zeros_initializer()) # normed_v = g * v / ||v|| normed_v = g * v * math_ops.rsqrt( math_ops.reduce_sum(math_ops.square(v))) return math_ops.reduce_sum(normed_v * math_ops.tanh(keys + processed_query + b), [2]) else: return math_ops.reduce_sum(v * math_ops.tanh(keys + processed_query), [2])
if attention_layer_size is not None: attention_layer_sizes = tuple(attention_layer_size if isinstance(attention_layer_size, (list, tuple)) else (attention_layer_size,)) if len(attention_layer_sizes) != len(attention_mechanisms): raise ValueError("If provided, attention_layer_size must contain exactly one integer per attention_mechanism, saw: %d vs %d" % (len(attention_layer_sizes), len(attention_mechanisms))) self._attention_layers = tuple(layers_core.Dense(attention_layer_size, name="attention_layer", use_bias=False, dtype=attention_mechanisms[i].dtype) for i, attention_layer_size in enumerate(attention_layer_sizes)) self._attention_layer_size = sum(attention_layer_sizes) else: self._attention_layers = None self._attention_layer_size = sum(attention_mechanism.values.get_shape()[-1].value for attention_mechanism in attention_mechanisms) for i, attention_mechanism in enumerate(self._attention_mechanisms): attention, alignments = _compute_attention(attention_mechanism, cell_output, previous_alignments[i], self._attention_layers[i] ifself._attention_layerselse None) alignment_history = previous_alignment_history[i].write(state.time, alignments) if self._alignment_history else()
alignment_history:即是否将之前的 alignments 存储到 state 中,以便于后期进行可视化展示。
Options: --update Update Pipenv & pip to latest. --where Output project home information. --venv Output virtualenv information. --py Output Python interpreter information. --envs Output Environment Variable options. --rm Remove the virtualenv. --bare Minimal output. --completion Output completion (to be eval'd). --man Display manpage. --three / --two Use Python 3/2 when creating virtualenv. --python TEXT Specify which version of Python virtualenv should use. --site-packages Enable site-packages for the virtualenv. --jumbotron An easter egg, effectively. --version Show the version and exit. -h, --help Show this message and exit.
Usage Examples: Create a new project using Python 3.6, specifically: $ pipenv --python 3.6
Install all dependencies for a project (including dev): $ pipenv install --dev
Create a lockfile containing pre-releases: $ pipenv lock --pre
Show a graph of your installed dependencies: $ pipenv graph
Check your installed dependencies for security vulnerabilities: $ pipenv check
Install a local setup.py into your virtual environment/Pipfile: $ pipenv install -e .
Commands: check Checks for security vulnerabilities and against PEP 508 markers provided in Pipfile. graph Displays currently–installed dependency graph information. install Installs provided packages and adds them to Pipfile, or (if none is given), installs all packages. lock Generates Pipfile.lock. open View a given module in your editor. run Spawns a command installed into the virtualenv. shell Spawns a shell within the virtualenv. uninstall Un-installs a provided package and removes it from Pipfile. update Uninstalls all packages, and re-installs package(s) in [packages] to latest compatible versions.
接下来我们首先验证一下当前的项目是没有创建虚拟环境的,调用如下命令:
1
pipenv --venv
结果如下:
1
No virtualenv has been created forthisproject yet!
Warning: the environment variable LANG is not set! We recommend setting this in ~/.profile (or equivalent) for proper expected behavior. Creating a virtualenv for this project… Using /usr/local/bin/python3 tocreate virtualenv… ⠋Running virtualenv with interpreter /usr/local/bin/python3 Using base prefix '/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6' New python executable in /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python3.6 Also creating executable in /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python Installing setuptools, pip, wheel...done. Virtualenv location: /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E
实际上这也和 virtualenv 激活的流程一样,也是调用了类似 source venv/bin/activate 方法将这个路径加到全局环境变量最前面,这样就会优先调用该路径下的 python、python3、python3.6 可执行文件了。 这时候我们会发现命令行的样子就变了,前面多了一个 (PipenvTest-VSTVh89E) 的标识,代表当前我们已经切换到了虚拟环境下。 这时我们用 which 或 where 命令查看一下 Python 可执行文件的路径,命令如下:
1 2 3 4 5 6
(PipenvTest-VSTVh89E) CQC-MAC% which python3 /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python3 (PipenvTest-VSTVh89E) CQC-MAC% which python3.6 /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python3.6 (PipenvTest-VSTVh89E) CQC-MAC% which python /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python
中文分词,即 Chinese Word Segmentation,即将一个汉字序列进行切分,得到一个个单独的词。表面上看,分词其实就是那么回事,但分词效果好不好对信息检索、实验结果还是有很大影响的,同时分词的背后其实是涉及各种各样的算法的。 中文分词与英文分词有很大的不同,对英文而言,一个单词就是一个词,而汉语是以字为基本的书写单位,词语之间没有明显的区分标记,需要人为切分。根据其特点,可以把分词算法分为四大类:
最大匹配法(MM)。基本思想是:假设自动分词词典中的最长词条所含汉字的个数为 i,则取被处理材料当前字符串序列中的前 i 个字符作为匹配字段,查找分词词典,若词典中有这样一个 i 字词,则匹配成功,匹配字段作为一个词被切分出来;若词典中找不到这样的一个 i 字词,则匹配失败,匹配字段去掉最后一个汉字,剩下的字符作为新的匹配字段,再进行匹配,如此进行下去,直到匹配成功为止。统计结果表明,该方法的错误率 为 1/169。
逆向最大匹配法(RMM)。该方法的分词过程与 MM 法相同,不同的是从句子(或文章)末尾开始处理,每次匹配不成功时去掉的是前面的一个汉字。统计结果表明,该方法的错误率为 1/245。
SnowNLP: Simplified Chinese Text Processing,可以方便的处理中文文本内容,是受到了 TextBlob 的启发而写的,由于现在大部分的自然语言处理库基本都是针对英文的,于是写了一个方便处理中文的类库,并且和 TextBlob 不同的是,这里没有用 NLTK,所有的算法都是自己实现的,并且自带了一些训练好的字典。GitHub地址:https://github.com/isnowfy/snownlp。
pip3 -V pip 9.0.1 from /usr/local/anaconda3/lib/python3.6/site-packages (python 3.6)
1 2 3 4 5 6 7
which python3 /usr/local/anaconda3/bin/python3 python3 Python 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 18:10:19) [GCC 7.2.0] on linux Type "help", "copyright", "credits"or"license"for more information. >>>
如果存在之前的旧版本,可以选择先卸载,以免和新的 CUDA 版本产生冲突,在 /usr/local/cuda/bin 目录下有一个 uninstallcuda*.pl 文件,可以直接运行卸载,命令如下:
1
sudo ./uninstall_cuda_*.pl
这样即可将 CUDA 全部卸载。 接下来我们再下载 CUDA 9.0,注意 TensorFlow 1.5 和 1.6 版本依然只是兼容 CUDA 9.0,没有兼容 CUDA 9.1,所以不要下载 9.1,CUDA 9.0 的下载地址是:https://developer.nvidia.com/cuda-90-download-archive,然后依次勾选好系统的版本,如图所示: 这里我们选择 Linux-x86_64-Ubuntu-16.04-runfile 的配置,然后点击 Base Installer 部分的 Download 按钮,下载 CUDA 9.0 安装包。 对应的下载命令是:
The NVIDIA CUDA Toolkit provides command-line and graphical tools for building, debugging and optimizing the performance Do you accept the previously read EULA? accept/decline/quit: accept
Install NVIDIA Accelerated Graphics Driver for Linux-x86_64 384.81? (y)es/(n)o/(q)uit: n
Install the CUDA 9.0 Toolkit? (y)es/(n)o/(q)uit: y
Enter Toolkit Location [ default is /usr/local/cuda-9.0 ]:
Do you want to install a symbolic link at /usr/local/cuda? (y)es/(n)o/(q)uit: y
Install the CUDA 9.0 Samples? (y)es/(n)o/(q)uit: y
Enter CUDA Samples Location [ default is /home/cqc ]:
Installing the CUDA Toolkit in /usr/local/cuda-9.0 ...
最后如果出现这样的提示,就证明 CUDA 安装好了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Driver: Not Selected Toolkit: Installed in /usr/local/cuda-9.0 Samples: Installed in /home/cqc, but missing recommended libraries
Please make sure that - PATH includes /usr/local/cuda-9.0/bin - LD_LIBRARY_PATH includes /usr/local/cuda-9.0/lib64, or, add /usr/local/cuda-9.0/lib64 to /etc/ld.so.conf and run ldconfig as root
To uninstall the CUDA Toolkit, run the uninstall script in /usr/local/cuda-9.0/bin
Please see CUDA_Installation_Guide_Linux.pdf in /usr/local/cuda-9.0/doc/pdf for detailed information on setting up CUDA.
***WARNING: Incomplete installation! This installation did not install the CUDA Driver. A driver of version at least 384.00 is required for CUDA 9.0 functionality to work. To install the driver using this installer, run the following command, replacing <CudaInstaller> with the name of this run file: sudo <CudaInstaller>.run -silent -driver
Linux version 4.4.0-112-generic (buildd@lgw01-amd64-010) (gcc version 5.4.020160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5) ) #135-Ubuntu SMP Fri Jan 1911:48:36 UTC 2018