最近的失误操作

最近操作失误了很多:

  • 创成长低卖,本来长期仓位,由于自己心理原因:怕跌,给卖掉了,卖了之后一直涨。
  • 上证50,深红利低卖高买,也是本来说好的长期持有仓位,却想着做个T,没想到白白把成本T高。
  • 还没有出信号的时候,提前买入上证50,但是当天却没有信号,买后就跌。但是当天有信号的两个标的,没钱买,这两个标的反而大涨。一来一回损失不少。

总结一下原因:

第一、划定为长期仓位的,自己没有信仰,在看盘的时候就想去操作一把,就导致反向操作的发生。以后长期仓位的平时不操作,只在有买入信号和卖出信号的时候操作。

第二、坚定执行策略,按信号买卖,把自己当成机器人即可,减少甚至做到不看盘,减少突发奇想的操作。

以后计划:

  1. 补齐上证50,红利,深红利,创成长,双创的长期持有仓位,做股权现金平衡,只在策略信号发生时候操作
  2. 坚定执行策略,按照信号买卖
2022年8月23日更新

截至今天,昨天错误买入的上证50,已经亏损1%,而如果买入新能车则盈利2.3%,一来一回损失3.3%收益率,啧啧。

2022年8月24日更新

今天在-0.29%的时候认亏服输卖掉了,这一波总共损失3.59%的收益率,好吧,也不算太惨重,及时止损。今天幸亏卖掉了,收盘的时候上证50跌1.01%。

2022年8月25日更新

MD今天早上也不知道哪里犯抽把红利在1%处给清仓了,后来涨了2.21%,我吐了,而且这个是长期仓位,为啥我要卖呢??

以后上面总结的两个原因和计划,切记切记。

要当爸爸了

媳妇怀孕现在快5周了,之前早孕试纸已经测出来了,今天去医院做了个B超,显示宫内孕,但是还属于比较早的时期,医生让2周后复查。

今年可算是事情比较多的一年了:

  • 交房装修,花钱费力(主要还是精力)
  • 房子租不成了,又得换
  • 怀孕

而这些都得一个人扛着,现在周末我就下厨房给媳妇做饭,还有考虑各种营养搭配,饮食也要健康。今天食谱:早上燕麦杂粮粥+饼+煎鸡蛋+生菜,下午面条+西兰花+胡萝卜+青菜+荷包蛋,晚上蒸了红薯和小南瓜。

目前还未决的事情有:

装修:卡到柜子这里了,等柜子来一装,门一装,插座灯装好,基本就差不多了。不过现在家具还没买,抽空还得去看看。

租房,确定了一家,130平一月4000块,但是家具简单,床是铁艺床,主要是考虑怀孕期间家里通风得好,然后家具少甲醛等有害物质也少。不过得等到9月10日才能搬,最近就先在这里继续狗着吧。

怀孕,最主要是保证媳妇的饮食健康,营养全面,至少得自己做饭吧,做一顿两顿好说,一直做饭洗碗,啧啧,是个挑战。最近疫情又开始肆虐,不知道什么时候是个头,疫情在平时倒也没啥,现在媳妇怀孕期间,就得特别小心了。

将来孩子出生了,就要做抉择了,如果公司能让我在家办公,就可以请一个保姆帮忙打理,要是不行,只能自己上了。经济问题只能期待A股了,希望FIRE基金能够稳定收益才行。哎,走着看吧。

克服中年危机

中产阶级的底座是「假设一切顺利」——这是小概率事件,从风控的角度看,底裤漏风。

现在能取得得高收入,应该多赚一年是一年,保持高储蓄,努力维持,而不是主动打破这种状态。

那么在40岁以前,主要做好三件事:

第一,大幅提高自己的“工资收入”;

第二,对投资有兴趣,开户越早越好,提升自己的投资能力,形成自己的盈利模式;

第三,要有一个好身体,不三高,不熬夜,吃得下,睡得着。

有此三项,保住中产阶级,克服中年危机应该也会有更强的底气了。

在这个社会上没有信任可言

2020年的时候我租下了这个房子,本来就是作为新房的过渡期居住,当时就想签三年合同,但是房东说最多只签两年,两年就两年吧,到时候再说。

转眼间到2022年了,房东当时是这么说的

image.png

当时我觉得:

  1. 签合同房东还得到房子来,我不喜欢被打扰
  2. 今年我的房子就装修好了,刚好到明年租房到期住进去,也就一年时间。

现在看来在这个社会上没有信任可言,一切嘴上说的都不可信,落实到文件上才是最真的事情

今天,房东给我下逐客令了,虽然比较委婉,但是就是那个意思。那现在这样对我的损害就是:

  1. 本来3200的房租,现在新租房要3500,一个月损失300,6个月损失1800
  2. 新找房要半个月中介费,损失1750
  3. 还要再搬家,劳心费神

所以我主张房东承担这个损失,但是他不行,他的意思就是他不可能承担这个损失。

我就说当初是你说的因为找中介麻烦,所以没有签合同,我们是基于基本的信任以及租房惯例(默认续期一年)来达成共识的。

但是我也知道,没有合同,我没法主张这个损失,在法理上站不住,因为:根据《合同法》第二百三十六条:“租赁期间届满,承租人继续使用租赁物,出租人没有提出异议的,原租赁合同继续有效,但租赁期限为不定期。“

不定期的意思就是房东随时可以不租,但是需要提前一个月通知,租客随时可以退租。

当然如果合同里有特殊规定的除外,比如到期后如果没有异议继续续一个合同期。

那现在我的这个情况就是之前合同找不到了,大概率是到期后重新签订合同。现在没有合同,所以就是不定期合同,房东现在的这个要求合法。

那我这里跟他沟通的重点就是 当初是你提出不签合同,我表示信任,也不想让你跑一趟,所以没签。用道德绑架房东,毕竟房东是银行工作人员,还是有一些羞耻感的。

最后达成的结果就是:房东补偿我1000元。

那我也只能见好就收了,然后尽快搬走,省的木乱了。

最后再强调一遍:在这个社会上没有信任可言,一切嘴上说的都不可信,落实到文件上才是最真的事情

投资就像在种树

突然觉得自己的账户就像是精心维护的果园,每一个标的就是一株树,建仓就是埋下种子,凡是要将树种高种大,必须有一套方法,加以耐心,毅力和时间,此后经受无数次风雨,依然坚持不懈,最终才能将树养大,养大之后便可以坐享果实了,正所谓前人栽树,后人乘凉。

对应投资一样,在建仓之后,悉心呵护等待其脱离成本区,有时候会接受大跌的洗礼,但是坚持投资策略,不断的给它输送养分,最终慢慢长大,积累利润垫,等积累足够的利润垫,市值也越来越大的时候,每年分红就够了,没有分红的就需要自己把握高点和低点,高点卖出,低点买入,类似于分红了,这时候就是乘凉的时候。

播种和收获都非常容易,但是用科学的方法呵护并陪伴其长大是一个漫长的过程,这不仅是一个技术活,也是一个考验耐心的体力活,期间不能着急,否则树折腾来折腾去,肯定就折腾死了。

立下种树这个观念,在投资中就不会急躁,不会被各种诱惑吸引,只要老老实实按自己方法种自己的树就可以了,保持耐心,相信将来的某一天终将可以品尝到收获的果实。把投资当成种树,也就是接受投资需要比较长的一段时间来发育,而不是一夜暴富,建立了这个心态,就不会一惊一乍,就会宠辱偕忘,波澜不惊。

我的果园现在有十三棵树,现在都在慢慢长大。树呢分为两类,一种是不太用打理的,它会自己慢慢长大;另一种是短期经常打理的,当然长期的也需要短期打理,但是总体上是省心的。现在大部分都还处于发芽期,而且很多处在风雨飘摇,因为自从种下之后经常都是暴风雨。希望以后情况能好一点,让我的树苗们茁壮成长。目前的目标是树苗们都可以长到10%的利润垫。

在 Hexo 博客里插入图表

用到了 Chartjs 的插件:hexo-tag-chart

用法总结如下:

首先在 hexo 博客的目录里运行:npm install hexo-tag-chart --save

然后在文章中就可以使用 chart 的 tag 了

1
2
3
{% chart 90% 300 %}
\\TODO option goes here
{% endchart %}

其中 chart 是标签名,endchart 是结束标签,不需要更改,90% 是图表容器的相对宽度,默认是 100%,300 是图表容器的高度,默认是按正常比例缩放的,你可以通过设置 options 里面的 aspectRatio 属性来调整宽高比例,另外还有许多属性可以自定义,你可以查看 官方文档。在标签之间的部分,需要自己填充的图表数据和属性。

我自己使用图表的页面:FIRE 基金

参考:

构建基于 Python 的单元测试

这篇文章记录了如何基于 Python 构建一个 Web 系统的单元测试,涉及一些基本和高级用法。

测试分类

  • 单元测试:单个模块的测试
  • 集成测试:多个模块的测试
  • 功能测试:项目的功能测试

其实就是范围不同,单元测试仅是系统特定一部分的测试,功能测试是将系统作为整体进行测试,集成测试介于两者之间。

单元测试库

最常用的是 unittest 和 pytest

  • 继承 unittest 的 TestCase 类来组织单元测试
  • assert 语句用来检测是否符合预期,而 pytest 提供了一些更强大的 assert 方法
  • pytest 用来运行测试,它可以使用加强版的 assert,并且它完全支持 unittest

一个简单的单元测试

1
2
3
4
5
6
7
8
9
import unittest
from fizzbuzz import fizzbuzz


class TestFizzBuzz(unittest.TestCase):
def test_fizz(self):
for i in [3, 6, 9, 18]:
print('testing', i)
assert fizzbuzz(i) == 'Fizz'

运行:

1
2
3
4
5
6
7
8
9
(venv) $ pytest
========================== test session starts ===========================
platform darwin -- Python 3.8.6, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /Users/miguel/testing
collected 1 items

test_fizzbuzz.py . [100%]

=========================== 1 passed in 0.03s ============================

pytest命令比较智能,它会自动识别单元测试,它假定以这样的名字:test_[something].py 或者 [something]_test.py 命名的模块都包含单元测试。同时它也会搜索子目录。

一般来说,单元测试统一放到 tests 目录下,和应用目录隔离开。

测试覆盖率

安装:pip install pytest-cov

运行 pytest --cov=fizzbuzz,可以针对 fizzbuzz 模块运行单元测试以及覆盖率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(venv) $ pytest --cov=fizzbuzz
========================== test session starts ===========================
platform darwin -- Python 3.8.6, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/miguel/testing
plugins: cov-2.11.1
collected 3 items

test_fizzbuzz.py ... [100%]

---------- coverage: platform darwin, python 3.8.6-final-0 -----------
Name Stmts Miss Cover
---------------------------------
fizzbuzz.py 13 4 69%
---------------------------------
TOTAL 13 4 69%


=========================== 3 passed in 0.07s ============================

还有以下参数:

  • --cov-branch 针对分支处理,有多少个分支就统计多少次
  • --cov-report=term-missing 表示以何种方式展示报告,term-missing表示在terminal上展示,并且会额外加上缺少测试覆盖的代码行数,另外一个常用选项是html 在html上展示报告,很清晰,常用。

可以添加注释 pragma: no cover 来跳过该块代码的覆盖率检测

测试参数化

使用库 parameterized: pip install parameterized

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from parameterized import parameterized

# ...

class TestLife(unittest.TestCase):
# ...

@parameterized.expand([('pattern1.txt',), ('pattern2.txt',)])
def test_load(self, pattern):
life = Life()
life.load(pattern)
assert life.survival == [2, 3]
assert life.birth == [3]
assert set(life.living_cells()) == {
(10, 10), (11, 11), (15, 10), (17, 10)}
assert life.bounding_box() == (10, 10, 17, 11)

也可以使用列表推导式:

1
2
3
4
5
class TestLife(unittest.TestCase):
# ...

@parameterized.expand([(n,) for n in range(9)])
def test_advance_cell(self, num_neighbors):

支持多参数:

1
2
3
4
5
6
7
import itertools

class TestLife(unittest.TestCase):
# ...

@parameterized.expand(itertools.product([True, False], range(9)))
def test_advance_cell(self, alive, num_neighbors):

测试异常

1
2
3
4
5
6
7
8
9
10
11
import pytest

# ...

class TestLife(unittest.TestCase):
# ...

def test_load_invalid(self):
life = Life()
with pytest.raises(RuntimeError):
life.load('pattern4.txt')

Mocking

mocking 就是劫持函数或者功能,可以控制返回值或者其他东西的一种功能。在测试中如果对某个函数已经有了详尽的测试,那么在这个函数被调用的地方,就可以用mocking功能,节约资源。

unittest 里的 mock 模块,可以使用 mock.patch_object() 来替换函数或者方法

1
2
3
4
5
6
7
8
9
from unittest import mock

class TestLife(unittest.TestCase):
# ...

@mock.patch.object(Life, '_advance_cell')
def test_advance_false(self, mock_advance_cell):
mock_advance_cell.return_value = False
# ...

测试 Web 应用

最好将测试归集到一个继承 unittest.TestCase 的类里,这样可以公用 setUp 和 tearDown 方法,会有更好的性能,以及更方便。

WSGI 和 ASGI 都有特定的规则用于服务器如何传递到应用的请求。所以我们可以注入假的请求到应用上来模拟,而不用启动真正的服务器。这些 Web 框架都有所谓的测试客户端(test clients)来帮助实现单元测试,不需要任何网络,会向应用传递假的请求。如果 Web 框架没有提供的话,WSGI 应用可以使用 Werkzeug 库,ASGI 应用可以使用 async-asgi-testclient

比如,Flask 框架可以直接使用自带的 test client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TestWebApp(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.appctx = self.app.app_context()
self.appctx.push()
db.create_all()
self.client = self.app.test_client()

def tearDown(self):
db.drop_all()
self.appctx.pop()
self.app = None
self.appctx = None
self.client = None

Tornado 框架可以继承 HTTPTestCase or AsyncHTTPTestCase 类来实现,其中它自带了 HTTPClient 和 AsyncHTTPClient,可以直接使用:

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
65
66
67
68
69
70
71
72
73
74
75

class BaseTestCase(AsyncHTTPTestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
self.db_session = test_session
self.db_session.commit()
self.cookie = SimpleCookie()

def get_app(self):
test_app = Application()
return test_app

def get_new_ioloop(self):
return IOLoop.current()

def get_url(self, path):
full_path = super(BaseTestCase, self).get_url('/api/v1{}'.format(path))
return full_path

def _update_cookies(self, headers):
try:
cookies = escape.native_str(headers['Set-Cookie'])
self.cookie.update(SimpleCookie(cookies))
except KeyError:
return

def make_response(self, req, resp):
response = Response()
response.status_code = getattr(resp, 'code', None)
response.headers = {k: v for k, v in list(resp.headers.items())}
response.encoding = get_encoding_from_headers(response.headers)
response.raw = resp
response.reason = response.raw.reason
response._content = resp.body

if isinstance(req.url, bytes):
response.url = req.url.decode('utf-8')
else:
response.url = req.url
extract_cookies_to_jar(response.cookies, req, resp)
response.request = req
return response

def send(self, url, method='GET', data=None, json_data=None, files=None, headers=None, **kwargs):
if 'follow_redirects' not in kwargs:
kwargs['follow_redirects'] = False
request = Request(url=self.get_url(url), files=files, data=data, json=json_data)
request_data = request.prepare()
if headers is None:
headers = {}
headers.update(request_data.headers)
cookie_sting = '; '.join([f'{key}={morsel.value}' for key, morsel in self.cookie.items()])
if cookie_sting != '':
headers.update({'Cookie': cookie_sting})
resp = self.fetch(url, method=method, headers=headers, body=request_data.body, allow_nonstandard_methods=True, **kwargs)
self._update_cookies(resp.headers)
response = self.make_response(request, resp)
self.db_session.rollback()
return response

def get(self, url, **kwargs):
response = self.send(url, method='GET', **kwargs)
return response

def patch(self, url, files=None, data=None, json_data=None):
response = self.send(url, method='PATCH', files=files, data=data, json_data=json_data)
return response

def post(self, url, files=None, data=None, json_data=None, **kwargs):
response = self.send(url, method='POST', files=files, data=data, json_data=json_data, **kwargs)
return response

def put(self, url, files=None, data=None, json_data=None):
response = self.send(url, method='PUT', files=files, data=data, json_data=json_data)
return response

测试 html 内容

没必要全部 match 去做测试,而是可以检查一部分内容是否存在,比如提交按钮是否存在于 html 中,而忽略其顺序等无关信息。

1
2
3
4
5
6
7
8
9
10
11
def test_registration_form(self):
response = self.client.get('/auth/register')
assert response.status_code == 200
html = response.get_data(as_text=True)

# make sure all the fields are included
assert 'name="username"' in html
assert 'name="email"' in html
assert 'name="password"' in html
assert 'name="password2"' in html
assert 'name="submit"' in html

这样的方式也适合于其他数据量比较大的测试,只需要测试关键部分即可。

提交表单

主要问题在于 CSRF token 怎么处理,可以先发一个 GET 请求,然后拿到 token,再去提交表单,这是一种方法。另一种方法就是在测试中禁掉 CSRF 的保护。

1
2
3
4
5
6
7
def setUp(self):
self.app = create_app()
self.app.config['WTF_CSRF_ENABLED'] = False # no CSRF during tests
self.appctx = self.app.app_context()
self.appctx.push()
db.create_all()
self.client = self.app.test_client()

测试表单验证

根据表单验证失败返回的语句进行判断

1
2
3
4
5
6
7
8
9
10
def test_register_user_mismatched_passwords(self):
response = self.client.post('/auth/register', data={
'username': 'alice',
'email': 'alice@example.com',
'password': 'foo',
'password2': 'bar',
})
assert response.status_code == 200
html = response.get_data(as_text=True)
assert 'Field must be equal to password.' in html

测试需要登陆验证的页面

有以下几点:

  1. setUp 方法初始化用户
  2. login 方法
  3. 完成对应测试

Example:

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

# ...
import re

class TestWebApp(unittest.TestCase):
# ...

def setUp(self):
self.app = create_app()
self.app.config['WTF_CSRF_ENABLED'] = False # no CSRF during tests
self.appctx = self.app.app_context()
self.appctx.push()
db.create_all()
self.populate_db()
self.client = self.app.test_client()

def populate_db(self):
user = User(username='susan', email='susan@example.com')
user.set_password('foo')
db.session.add(user)
db.session.commit()

def login(self):
self.client.post('/auth/login', data={
'username': 'susan',
'password': 'foo',
})

def test_write_post(self):
self.login()
response = self.client.post('/', data={'post': 'Hello, world!'},
follow_redirects=True)
assert response.status_code == 200
html = response.get_data(as_text=True)
assert 'Your post is now live!' in html
assert 'Hello, world!' in html
assert re.search(r'<span class="user_popup">\s*'
r'<a href="/user/susan">\s*'
r'susan\s*</a>\s*</span>\s*said', html) is not None

测试 API 服务器

比较简单,因为 API 接口第一涉及范围小,第二返回基本上都是 JSON,容易解析。

1
2
3
4
5
6
7
8
9
10
11
12
def test_api_register_user(self):
response = self.client.post('/api/users', json={
'username': 'bob',
'email': 'bob@example.com',
'password': 'bar'
})
assert response.status_code == 201

# make sure the user is in the database
user = User.query.filter_by(username='bob').first()
assert user is not None
assert user.email == 'bob@example.com'

参考

构建你的投资体系

作为一个普通人,最开始的时候需要通过工作不断的积累本金,应付结婚买房生子等等诸多事情,而这些事情都是相当耗费钱的。一旦度过这些所谓的“坎”,工作上也有了不断进步,收入逐渐提高,财富也慢慢的积累起来的时候,就是时候考虑如何用这些积累的财富去创造价值了。

使用资本去创造额外价值就是一种投资,但是投资是有巨大风险的,在没有清楚认识到这些风险之前,不要投资!你要做的就是构建属于你的投资体系!

这篇文章我先介绍一下自己的投资体系,至于我是怎么一步一步的构建出来的,以后再说。

建立在股票基金上

投资体系这件事情范围很大,涉及整个家庭的资金分配,最为人熟知的就是,标准普尔家庭资产象限图

image.png

我这里指的是狭义上的投资体系:股票基金这种高风险投资标的上建立的投资体系

基本原则

首先我的投资体系里有几个原则:

  1. 不买股票,只买基金。其实对于我来说,我的投资体系是建立在基金上的。
  2. 建立自己的交易系统并且坚定执行。

体系结构

我的投资体系分为 趋势梭哈长期持有 两个部分,但是这两个部分共同点是基于一套交易系统。

这个交易系统是我花了一年时间打造的量化决策交易系统。

趋势梭哈

趋势梭哈部分的主要理念就是利用交易系统的信号进行高抛低吸

分为 梭哈标的打野标的

梭哈标的目前就是创业板相关基金,创成长,创业板50,双创,在买入的时候全仓买入。

打野标的就是其他一些宽基和行业基金:上证50,红利,深红利,科创50,能源,新能车,银行,农业,军工,旅游,有色金属。买入的时候只买20%比例。

如何做呢?就是在交易系统提示弱转强的时候买入,在提示强转弱的时候卖出,或者是根据交易系统的止盈条件进行卖出。

长期持有

长期持有部分就是长期持有一部分宽基,在低位加仓,在高位减仓,其实总体上就是一个大波段的高抛低吸。

目前持有标的:上证50,红利,深红利,创成长

另外长期持有的一个目标就是积累足够的份额,足够的利润垫,每年赚取分红,红利ETF的分红大概在3%-5%之间,还是可以的。

总结

我的投资体系大概就是如此,不必盯盘,因为有量化交易系统。不必焦虑,因为有投资体系。靠时间的力量,慢慢积累。

投资切记不要贪婪

曾经的贪婪

以我自身来说,很早就定下来了决定要一以贯之的投资策略,但是中间随着时间的发展,来自人天生的原始力量——贪婪,针对这最初的投资策略,做了好几次优化。

但是结果呢,每次优化每次坑,最终还是回到了之前的投资策略上,这中间的资金成本,时间成本,没法算了。

第一次,想能多上几次车,修改了策略的上车和下车条件。巧的是,修改之后第一次执行就遇到亏损情况,这次影响最为深远,亏损幅度也是最大。

第二次,想着能快速的盈利,将不要买股票这条原则忘的一干二净,买了不少股票,而且还是地产银行相关的,随后的事情——保交楼事件,让银行地产相关的股票大幅下挫,但是还好动用的仓位不多,这次亏损幅度较小。

第三次,再一次的修改了策略的上车,下车条件,按道理这次改变其实不算很大,但是恰巧的是,刚一改就遇到不相容的情况,造成了损失,亏损幅度也较小。

综上,三次都是为了多挣点钱,不满足于既定策略的收益情况,做了一些贪婪的决策,最终导致了亏钱,亏时间的事情发生。

这三次比较巧的事情是,每次都是刚修改完,立马就会步入坑中,其实这三个修改,也是多多少少做了些回测的,但是很奇怪,每次修改完,不相容的情况立马就会出现,立马就会造成亏损。

还好的是,每次跳坑之后,切换到原始策略上,很快就又都能回血,但是失去的时间已经一去不复返了。

还有一种贪婪就是不按策略既定的止盈条件来操作,自己觉得还要涨,所以就不卖了,导致本来还能盈利的,后来给亏损了。这种情况的贪婪伤害幅度虽然不大,但是侮辱性极强,很气人。

总结

首先如果真的要做优化,最好还是:

第一,使用模拟盘或者小仓位去测试

第二,要经历一个跨度比较长的时间

第三,要严格做出回测

否则,脑袋一拍,就动用大仓位去试错,后果可想而知。

其次严格按策略的止盈止损来操作,既然已经有策略,不按策略来,反而去拍脑袋决策,孰优孰劣可想而知。

最后投资要记的一点:慢就是快,能不断的滚雪球,就是最好的方式。切记不要贪婪

pip 最佳使用方法

image.png

在激活的虚拟环境中使用 pip install 或者 python -m pip install 效果是完全相同的,但是有些场景下就有问题了,而 python -m pip 确保了想要安装的包会和当前解释器是一个环境。

可以设置个别名接着用:

1
❯ echo 'alias pip="python -m pip"' >> ~/.aliasrc