Algorithm
本周选择的算法题是:Merge Two Sorted Lists
规则如下:
Merge two sorted linked lists and return it as a new sorted list. The new list should be made by splicing together the nodes of the first two lists.
Example 1:
Input: l1 = [1,2,4], l2 = [1,3,4]
Output: [1,1,2,3,4,4]
Example 2:
Input: l1 = [], l2 = []
Output: []
Example 3:
Input: l1 = [], l2 = [0]
Output: [0]
Constraints:
- The number of nodes in both lists is in the range
[0, 50]
. -100 <= Node.val <= 100
- Both
l1
andl2
are sorted in non-decreasing order.
Solution
递归
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if not l1: return l2
if not l2: return l1
if l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
迭代
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
head = ListNode()
curr = head
while l1 and l2:
if l1.val < l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
curr.next = l1 if l1 else l2
return head.next
Review
市面上有许多 Python 虚拟环境管理工具:
- venv
- poetry
- pipenv
- …
如作者所说,这些工具在解决环境隔离的同时引入了以下新的问题:
- 额外的学习曲线
终端隔离 - 在每个终端里都要做一遍重复的操作
- 认知负担 - 你需要记得 activating / deactivating、环境安装位置等
虽然 PEP 582 仍处于草拟阶段,但可能是一个答案:
- 将包安装到工程根目录下的
__pypackages__
中 - 导包时优先导入
__pypackages__
中的包 - 因为是语言级支持,不再需要额外的工具和认知负担
不过想真正做到这些,实际上是将包管理工具和虚拟环境工具合二为一了。
Tip
海象操作符
# python3.8
if (test := "123 ".strip()) == "123":
print(test)
表达式多了一个副作用:为目标赋值。
Share
分享一段通过 Python 管理 Provisioning Profile 的脚本吧,提供了以下能力:
- 查询已注册的设备
- 注册设备
- 查询 Profile
- 创建 Profile
- 删除 Profile
- 下载 Profile
代码如下:
Device = collections.namedtuple("Device", [
"addedDate", # 添加日期
"name", # 名称
"deviceClass", # 设备类型
"model", # 设备型号
"id", # 唯一标识符
"udid", # 设备标识符
"status", # 启用状,ENABLED/DISABLED
])
Profile = collections.namedtuple("Profile", [
"type", # 类型
"id", # 唯一标识符
"bundle_id", # 包名
"certificates", # 证书
])
def generate_jwt(config: dict) -> str:
"""
生成 JWT Token
config: {
issuer_id
key_id
key_dir # 密钥文件所在的目录
}
"""
print("开始生成 JWT Token...")
issuer_id = config["issuer_id"]
key_id = config["key_id"]
key_dir = os.path.join(os.path.dirname(__file__), config["key_dir"])
command = "xctoken generate"
process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, env=dict(os.environ, ISSUER_ID=issuer_id, KEY_ID=key_id, KEY_DIR=key_dir))
process.wait()
token = process.stdout.read().decode("utf-8").strip()
print("生成成功")
return token
def make_request(func, token: str, url: str, params: dict = None) -> dict:
response = func(url, headers={
"Authorization": f"Bearer {token}"
}, json=params)
if response.status_code // 10 == 20 :
if len(response.text) >= 2:
return response.json()
else:
return response.text
else:
raise Exception(f"网络请求失败: {url}, {response.content}")
def fetch_devices(token: str) -> [Device]:
"""
获取所有已注册的设备
"""
print("开始获取目前所有已注册的设备...")
response = make_request(requests.get, token, "https://api.appstoreconnect.apple.com/v1/devices?limit=100")
raw_devices = response["data"]
devices = [Device(*(raw_device[k] if k in raw_device else raw_device["attributes"][k] for k in Device._fields)) for raw_device in raw_devices]
print(f"获取成功,当前已注册设备数量为{len(devices)}")
return devices
def register_device(token: str, name: str, udid: str) -> str:
"""
注册设备
"""
print("正在注册设备...")
response = make_request(requests.post, token, "https://api.appstoreconnect.apple.com/v1/devices", params={
"data": {
"attributes": {
"name": name,
"platform": "IOS",
"udid": udid,
}, "type": "devices"
}
})
print("注册成功")
return response["data"]["id"]
def create_and_download_profile(token: str, name: str, type: str, bundle_id: str, devices: [dict], certificates: [dict]):
"""
创建并下载 Profile
"""
response = make_request(requests.post, token, "https://api.appstoreconnect.apple.com/v1/profiles", params={
"data": {
"attributes": {
"name": name,
"profileType": type
}, "relationships": {
"bundleId": {
"data": bundle_id
},
"certificates": {
"data": certificates
},
"devices": {
"data": devices
},
}, "type": "profiles"
}
})
attributes = response["data"]["attributes"]
profileContent = base64.b64decode(attributes["profileContent"])
# 下载到当前文件所在的目录
path = os.path.join(os.path.dirname(__file__), f"{name}.mobileprovision")
with open(path, 'wb') as file:
file.write(profileContent)
def delete_profile(token: str, profile_id: str):
"""
删除 Profile
"""
print("准备删除旧的 Profile...")
make_request(requests.delete, token, f"https://api.appstoreconnect.apple.com/v1/profiles/{profile_id}")
print("删除成功")
def query_profile(token: str, name: str) -> dict:
"""
获取 Profile 信息
"""
print(f"开始获取 Profile 信息: {name}")
response = make_request(requests.get, token, f"https://api.appstoreconnect.apple.com/v1/profiles?filter[name]={name}&fields[profiles]=certificates,uuid,bundleId,profileType&include=certificates,bundleId&fields[bundleIds]=identifier")
raw_profile = response["data"][0]
certificates = raw_profile["relationships"]["certificates"]["data"]
profile_type = raw_profile["attributes"]["profileType"]
profile_id = raw_profile["id"]
profile_bundle_id = raw_profile["relationships"]["bundleId"]["data"]
profile = Profile(profile_type, profile_id, profile_bundle_id, certificates)
print("获取成功")
return profile
封装一层简单的 wrapper 即可。