分类 软件开发 下的文章

你是如何在 Linux 上使用 C 编写你的程序的?它确实是非常简单的,由三个简单的步骤组成。

步骤 1: 编写你的 C 程序,并使用一个 .c 的扩展名进行保存。例如,my_program.c

步骤 2: 在一个终端中使用 gcc 编译器来编译程序并生成目标文件,像这样:

gcc -o my_program my_program.c

步骤 3: 在 Linux 中,你可以以运行生成的对象文件的方式来运行你的 C 程序:

./my_program

这只是如何在 Linux 中编译和运行 C 程序的简要总结。假设你是 C 语言或 Linux 系统的新手,我将仔细演示这些步骤,以便你能在 Linux 环境中舒服地编写 C 程序。

事实上,我将讨论如何在 Linux 终端中以及在代码编辑器中运行 C 程序的两种方式。

方法 1: 在 Linux 终端中运行 C 程序

为了在 Linux 中运行一个 C 程序,你需要在你的系统上有一个 C 编译器。最流行的编译器是 gcc GNU 编译器套件 GNU Compiler Collection )。

你可以使用你发行版的软件包管理器来安装 gcc 。在基于 Debian 和 Ubuntu 的 Linux 发行版中,使用 apt 命令:

sudo apt install gcc

切换到保存你的 C 程序的目录(或者提供路径),然后通过编译程序生成对象文件:

gcc -o my_program my_program.c

记住,提供输出对象文件(-o my_program)是可选的。如果你不提供,那么将自动生成一个名称为 a.out 的对象文件。但是这样并不好,因为编译每个 C 程序都会覆盖它,而且你也不知道这个 a.out 对象文件究竟属于哪个程序。

在你的对象文件生成后,运行它来运行 C 程序。它已经能够执行了。像这样简单地使用它:

./my_program

接下来,如果你的程序是正确的,它将显示出你所期望的输出。正如你所看到的,这和 在 Linux 中运行 C++ 程序 没什么不同。

每更改一次你的程序,你都必须先重新编译它,然后再次运行生成的对象文件来运行这个新的 C 程序。

方法 2: 如何在 Linux 中使用一个诸如 VSCode 之类的代码编辑器来运行 C 程序

并不是每一个人都能适应命令行和终端,我完全理解这一点。

你可以使用一个诸如 Eclipse 或 Code Blocks 之类的真正的 C/C++ IDE ,但是它们是很重量级的程序,通常更适合于大型的项目。

我建议使用一个开源的代码编辑器,像 VSCode 或 Atom 。它们基本上是文本编辑器,但是你可以通过安装附加组件来直接在图形化的代码编辑器中编译和运行程序。

在这个示例中,我使用 VSCode 编辑器。它是一个来自微软的 非常流行的开源的代码编辑器

首先,在 Ubuntu 的 软件中心中安装 VSCode 。对于其它发行版来说,请检查你的 Linux 发行版的软件包管理器或软件中心。你可以参看它的官方网站来查看更多的信息。

启动 VSCode ,打开或创建一个工程,在这里创建你的 C 程序。我使用一个简单的 Hello World 程序作为示例。

你必须确保你已经在你的 Linux 系统上安装了 gcc 编译器。

sudo apt install gcc

接下来你要做的事是使用一个允许你运行 C 代码的扩展。微软可能会提示你安装它的 C/C++ 程序扩展,但它的设置很复杂,因此我不推荐。

相反,我建议你使用 Code Runner 扩展。它是一个简单直接的扩展,你可以在不使用额外配置的情况下轻松地运行 C 和 C++ 代码。

转到扩展标签页,在其中搜索和安装 “Code Runner” 。

安装 Code Runner 扩展来运行 C/C++ 程序

重新启动 VSCode 。现在,你能够使用下面方法中的其中一个来运行 C 代码:

  • 使用快捷键 Ctrl+Alt+N
  • 按下 F1 ,接下来选择或输入 “Run Code” 。
  • 在文本编辑器中右键单击,从上下文菜单中单击 “Run code” 。

右键单击程序文件,然后选择 Run Code

当你运行这个 C 程序时,它将会被自动编译和运行。你可以在编辑器底部打开的终端中看到输出。还有比这更好的事情吗?

程序输出显示在编辑器的底部

你更喜欢哪一种方法?

在 Linux 命令行中运行一些 C 程序是没有问题的,但是使用一个代码编辑器会更容易一些,而且会节省时间。你不觉得吗?

你可以自己决定想使用哪一种方法。


via: https://itsfoss.com/run-c-program-linux/

作者:Abhishek Prakash 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用这些开源框架创建一个彩色地图,显示病毒的可能的传播路径。

对于一个全球旅行司空见惯的世界来说,疾病的传播是一个真正令人担忧的问题。一些组织会跟踪重大的流行病(还有所有普遍的流行病),并将他们的跟踪工作获得的数据公开出来。不过,这些原始的数据对人来说可能很难处理,这就是为什么数据科学如此重要的原因。比如,用 Python 和 Pandas 可视化 COVID-19 在全球范围内的传播路径可能对这些数据的分析有所帮助。

最开始,当面对如此大数量的原始数据时可能难以下手。但当你开始处理数据之后,慢慢地就会发现一些处理数据的方式。下面是用于处理 COVID-19 数据的一些常见的情况:

  1. 从 GitHub 上下载 COVID-19 的国家每日传播数据,保存为一个 Pandas 中的 DataFrame 对象。这时你需要使用 Python 中的 Pandas 库。
  2. 处理并清理下载好的数据,使其满足可视化数据的输入格式。所下载的数据的情况很好(数据规整)。这个数据有一个问题是它用国家的名字来标识国家,但最好是使用三位数的 ISO 3 码(国家代码表)来标识国家。为了生成 ISO 3 码,可是使用 pycountry 这个 Python 库。生成了这些代码之后,可以在原有的 DataFrame 上增加一列,然后用这些代码填充进去。
  3. 最后为了实现可视化,使用 Plotly 库中的 express 模块。这篇文章是使用名为choropleth 的地图(可在 Plotly 库中获得)来可视化该疾病在全球的传播。

第一步:Corona 数据

从下面这个网站上下载最新的 corona 数据(LCTT 译注:2020-12-14 仍可访问,有墙):

我们之间将这个下载好的数据载入为 Pandas 的 DataFrame。Pandas 提供了一个函数, read_csv(),可以直接使用 URL 读取数据,并返回一个 DataFrame 对象,具体如下所示:

import pycountry
import plotly.express as px
import pandas as pd
URL_DATASET = r'https://raw.githubusercontent.com/datasets/covid-19/master/data/countries-aggregated.csv'
df1 = pd.read_csv(URL_DATASET)
print(df1.head(3))  # Get first 3 entries in the dataframe
print(df1.tail(3))  # Get last 3 entries in the dataframe

在 Jupyter 上的输出截图:

 title=

从这个输出可以看到这个 DataFrame(df1)包括以下几列数据:

  1. Date
  2. Country
  3. Confirmed
  4. Recovered
  5. Dead

之后还可以看到 Date 这一列包含了从 1 月 22 日到 3 月 31 日的条目信息。这个数据是每天更新的,所以你会得到你当天的值。

第二步:清理和修改 DataFrame

我们要往这个 DataFrame 中增加一列数据,就是那个包含了 ISO 3 编码。可以通过以下三步完成这个任务:

  1. 创建一个包含所有国家的列表。因为在 df1Country 列中,国家都是每个日期就重复一次。所以实际上 Country 列中对每个国家就会有多个条目。我使用 unique().tolist() 函数完成这个任务。
  2. 我使用 d_country_code 字典对象(初始为空),然后将其键设置为国家的名称,然后它的值设置为其对应的 ISO 3 编码。
  3. 我使用 pycountry.countries.search_fuzzy(country) 为每个国家生成 ISO 3 编码。你需要明白的是这个函数的返回值是一个 Country 对象的列表。我将这个函数的返回值赋给 country_data 对象。以这个对象的第一个元素(序号 0)为例。这个 \ 对象有一个 alpha_3 属性。所以我使用 country_data[0].alpha_3 就能“获得”第一个元素的 ISO 3 编码。然而,在这个 DataFrame 中有些国家的名称可能没有对应的 ISO 3 编码(比如有争议的领土)。那么对这些“国家/地区”,我就用一个空白字符串来替代 ISO 3 编码。你也可以用一个 try-except 代码来替换这部分。except 中的语句可以写:print(‘could not add ISO 3 code for ->', country)。这样就能在找不到这些“国家/地区”对应的 ISO 3 编码时给出一个输出提示。实际上,你会发现这些“国家/地区”会在最后的输出中用白色来表示。
  4. 在获得了每个国家的 ISO 3 编码(有些是空白字符串)之后,我把这些国家的名称(作为键)还有国家对应的 ISO 3 编码(作为值)添加到之前的字典 d_country_code 中。可以使用 Python 中字典对象的 update() 方法来完成这个任务。
  5. 在创建好了一个包含国家名称和对应 ISO 3 编码的字典之后,我使用一个简单的循环将他们加入到 DataFrame 中。

第三步:使用 Plotly 可视化传播路径

choropleth 地图是一个由彩色多边形组成的地图。它常常用来表示一个变量在空间中的变化。我们使用 Plotly 中的 px 模块来创建 choropleth 图,具体函数为:px.choropleth

这个函数的所包含的参数如下:

plotly.express.choropleth(data_frame=None, lat=None, lon=None, locations=None, locationmode=None, geojson=None, featureidkey=None, color=None, hover_name=None, hover_data=None, custom_data=None, animation_frame=None, animation_group=None, category_orders={}, labels={}, color_discrete_sequence=None, color_discrete_map={}, color_continuous_scale=None, range_color=None, color_continuous_midpoint=None, projection=None, scope=None, center=None, title=None, template=None, width=None, height=None)

choropleth() 这个函数还有几点需要注意:

  1. geojson 是一个 geometry 对象(上面函数第六个参数)。这个对象有点让人困扰,因为在函数文档中没有明确地提到这个对象。你可以提供,也可以不提供 geojson 对象。如果你提供了 geojson 对象,那么这个对象就会被用来绘制地球特征,如果不提供 geojson 对象,那这个函数默认就会使用一个内建的 geometry 对象。(在我们的实验中,我们使用内建的 geometry 对象,因此我们不会为 geojson 参数提供值)
  2. DataFrame 对象有一个 data_frame 属性,在这里我们先前就提供了一个我们创建好的df1
  3. 我们用 Confirmed(确诊数)来决定每个国家多边形的颜色。
  4. 最后,我们 Date 列创建一个 animation_frame。这样我们就能通过日期来划分数据,国家的颜色会随着 Confirmed 的变化而变化。

最后完整的代码如下:

import pycountry
import plotly.express as px
import pandas as pd
# ----------- Step 1 ------------
URL_DATASET = r'https://raw.githubusercontent.com/datasets/covid-19/master/data/countries-aggregated.csv'
df1 = pd.read_csv(URL_DATASET)
# print(df1.head) # Uncomment to see what the dataframe is like
# ----------- Step 2 ------------
list_countries = df1['Country'].unique().tolist()
# print(list_countries) # Uncomment to see list of countries
d_country_code = {}  # To hold the country names and their ISO
for country in list_countries:
    try:
        country_data = pycountry.countries.search_fuzzy(country)
        # country_data is a list of objects of class pycountry.db.Country
        # The first item  ie at index 0 of list is best fit
        # object of class Country have an alpha_3 attribute
        country_code = country_data[0].alpha_3
        d_country_code.update({country: country_code})
    except:
        print('could not add ISO 3 code for ->', country)
        # If could not find country, make ISO code ' '
        d_country_code.update({country: ' '})

# print(d_country_code) # Uncomment to check dictionary  

# create a new column iso_alpha in the df
# and fill it with appropriate iso 3 code
for k, v in d_country_code.items():
    df1.loc[(df1.Country == k), 'iso_alpha'] = v

# print(df1.head)  # Uncomment to confirm that ISO codes added
# ----------- Step 3 ------------
fig = px.choropleth(data_frame = df1,
                    locations= "iso_alpha",
                    color= "Confirmed",  # value in column 'Confirmed' determines color
                    hover_name= "Country",
                    color_continuous_scale= 'RdYlGn',  #  color scale red, yellow green
                    animation_frame= "Date")

fig.show()

你可以从这里下载并运行完整代码

最后,这里还有一些关于 Plotly 绘制 choropleth 图的不错的资源。


via: https://opensource.com/article/20/4/python-map-covid-19

作者:AnuragGupta 选题:lujun9972 译者:zhangxiangping 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在过去的几周里,我花了很多时间用 PyTorch 实现了一个 char-rnn 的版本。我以前从未训练过神经网络,所以这可能是一个有趣的开始。

这个想法(来自 循环神经网络的不合理效应)可以让你在文本上训练一个基于字符的 循环神经网络 recurrent neural network (RNN),并得到一些出乎意料好的结果。

不过,虽然没有得到我想要的结果,但是我还是想分享一些示例代码和结果,希望对其他开始尝试使用 PyTorch 和 RNN 的人有帮助。

这是 Jupyter 笔记本格式的代码:char-rnn in PyTorch.ipynb。你可以点击这个网页最上面那个按钮 “Open in Colab”,就可以在 Google 的 Colab 服务中打开,并使用免费的 GPU 进行训练。所有的东西加起来大概有 75 行代码,我将在这篇博文中尽可能地详细解释。

第一步:准备数据

首先,我们要下载数据。我使用的是 古登堡项目 Project Gutenberg 中的这个数据:Hans Christian Anderson’s fairy tales

!wget -O fairy-tales.txt

这个是准备数据的代码。我使用 fastai 库中的 Vocab 类进行数据处理,它能将一堆字母转换成“词表”,然后用这个“词表”把字母变成数字。

之后我们就得到了一个大的数字数组(training_set),我们可以用于训练我们的模型。

from fastai.text import *
text = unidecode.unidecode(open('fairy-tales.txt').read())
v = Vocab.create((x for x in text), max_vocab=400, min_freq=1)
training_set = torch.Tensor(v.numericalize([x for x in text])).type(torch.LongTensor).cuda()
num_letters = len(v.itos)

第二步:定义模型

这个是 PyTorch 中 LSTM 类的封装。除了封装 LSTM 类以外,它还做了三件事:

  1. 对输入向量进行 one-hot 编码,使得它们具有正确的维度。
  2. LSTM 层后一层添加一个线性变换,因为 LSTM 输出的是一个长度为 hidden_size 的向量,我们需要的是一个长度为 input_size 的向量这样才能把它变成一个字符。
  3. LSTM 隐藏层的输出向量(实际上有 2 个向量)保存成实例变量,然后在每轮运行结束后执行 .detach() 函数。(我很难解释清 .detach() 的作用,但我的理解是,它在某种程度上“结束”了模型的求导计算)(LCTT 译注:detach() 函数是将该张量的 requires_grad 参数设置为 False,即反向传播到该张量就结束。)
class MyLSTM(nn.Module):
    def __init__(self, input_size, hidden_size):
        super().__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.h2o = nn.Linear(hidden_size, input_size)
        self.input_size=input_size
        self.hidden = None

    def forward(self, input):
        input = torch.nn.functional.one_hot(input, num_classes=self.input_size).type(torch.FloatTensor).cuda().unsqueeze(0)
        if self.hidden is None:
            l_output, self.hidden = self.lstm(input)
        else:
            l_output, self.hidden = self.lstm(input, self.hidden)
        self.hidden = (self.hidden[0].detach(), self.hidden[1].detach())

        return self.h2o(l_output)

这个代码还做了一些比较神奇但是不太明显的功能。如果你的输入是一个向量(比如 [1,2,3,4,5,6]),对应六个字母,那么我的理解是 nn.LSTM 会在内部使用沿时间反向传播更新隐藏向量 6 次。

第三步:编写训练代码

模型不会自己训练的!

我最开始的时候尝试用 fastai 库中的一个辅助类(也是 PyTorch 中的封装)。我有点疑惑因为我不知道它在做什么,所以最后我自己编写了模型训练代码。

下面这些代码(epoch() 方法)就是有关于一轮训练过程的基本信息。基本上就是重复做下面这几件事情:

  1. 往 RNN 模型中传入一个字符串,比如 and they ought not to teas。(要以数字向量的形式传入)
  2. 得到下一个字母的预测结果
  3. 计算 RNN 模型预测结果和真实的下一个字母之间的损失函数(e,因为 tease 这个单词是以 e 结尾的)
  4. 计算梯度(用 loss.backward() 函数)
  5. 沿着梯度下降的方向修改模型中参数的权重(用 self.optimizer.step() 函数)
class Trainer():
  def __init__(self):
      self.rnn = MyLSTM(input_size, hidden_size).cuda()
      self.optimizer = torch.optim.Adam(self.rnn.parameters(), amsgrad=True, lr=lr)
  def epoch(self):
      i = 0
      while i < len(training_set) - 40:
        seq_len = random.randint(10, 40)
        input, target = training_set[i:i+seq_len],training_set[i+1:i+1+seq_len]
        i += seq_len
        # forward pass
        output = self.rnn(input)
        loss = F.cross_entropy(output.squeeze()[-1:], target[-1:])
        # compute gradients and take optimizer step
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

使用 nn.LSTM 沿着时间反向传播,不要自己写代码

开始的时候我自己写代码每次传一个字母到 LSTM 层中,之后定期计算导数,就像下面这样:

for i in range(20):
    input, target = next(iter)
    output, hidden = self.lstm(input, hidden)
loss = F.cross_entropy(output, target)
hidden = hidden.detach()
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

这段代码每次传入 20 个字母,每次一个,并且在最后训练了一次。这个步骤就被称为沿时间反向传播,Karpathy 在他的博客中就是用这种方法。

这个方法有些用处,我编写的损失函数开始能够下降一段时间,但之后就会出现峰值。我不知道为什么会出现这种现象,但之后我改为一次传入 20 个字符到 LSTM 之后(按 seq_len 维度),再进行反向传播,情况就变好了。

第四步:训练模型!

我在同样的数据上重复执行了这个训练代码大概 300 次,直到模型开始输出一些看起来像英文的文本。差不多花了一个多小时吧。

这种情况下我也不关注模型是不是过拟合了,但是如果你在真实场景中训练模型,应该要在验证集上验证你的模型。

第五步:生成输出!

最后一件要做的事就是用这个模型生成一些输出。我写了一个辅助方法从这个训练好的模型中生成文本(make_predsnext_pred)。这里主要是把向量的维度对齐,重要的一点是:

output = rnn(input)
prediction_vector = F.softmax(output/temperature)
letter = v.textify(torch.multinomial(prediction_vector, 1).flatten(), sep='').replace('_', ' ')

基本上做的事情就是这些:

  1. RNN 层为字母表中的每一个字母或者符号输出一个数值向量(output)。
  2. 这个 output 向量并不是一个概率向量,所以需要 F.softmax(output/temperature) 操作,将其转换为概率值(也就是所有数值加起来和为 1)。temperature 某种程度上控制了对更高概率的权重,在限制范围内,如果设置 temperature=0.0000001,它将始终选择概率最高的字母。
  3. torch.multinomial(prediction_vector) 用于获取概率向量,并使用这些概率在向量中选择一个索引(如 12)。
  4. v.textify12 转换为字母。

如果我们想要处理的文本长度为 300,那么只需要重复这个过程 300 次就可以了。

结果!

我把预测函数中的参数设置为 temperature = 1 得到了下面的这些由模型生成的结果。看起来有点像英语,这个结果已经很不错了,因为这个模型要从头开始“学习”英语,并且是在字符序列的级别上进行学习的。

虽然这些话没有什么含义,但我们也不知道到底想要得到什么输出。

“An who was you colotal said that have to have been a little crimantable and beamed home the beetle. “I shall be in the head of the green for the sound of the wood. The pastor. “I child hand through the emperor’s sorthes, where the mother was a great deal down the conscious, which are all the gleam of the wood they saw the last great of the emperor’s forments, the house of a large gone there was nothing of the wonded the sound of which she saw in the converse of the beetle. “I shall know happy to him. This stories herself and the sound of the young mons feathery in the green safe.”

“That was the pastor. The some and hand on the water sound of the beauty be and home to have been consider and tree and the face. The some to the froghesses and stringing to the sea, and the yellow was too intention, he was not a warm to the pastor. The pastor which are the faten to go and the world from the bell, why really the laborer’s back of most handsome that she was a caperven and the confectioned and thoughts were seated to have great made

下面这些结果是当 temperature=0.1 时生成的,它选择字符的方式更接近于“每次都选择出现概率最高的字符”。这就使得输出结果有很多是重复的。

ole the sound of the beauty of the beetle. “She was a great emperor of the sea, and the sun was so warm to the confectioned the beetle. “I shall be so many for the beetle. “I shall be so many for the beetle. “I shall be so standen for the world, and the sun was so warm to the sea, and the sun was so warm to the sea, and the sound of the world from the bell, where the beetle was the sea, and the sound of the world from the bell, where the beetle was the sea, and the sound of the wood flowers and the sound of the wood, and the sound of the world from the bell, where the world from the wood, and the sound of the

这段输出对这几个单词 beetlesconfectionerssunsea 有着奇怪的执念。

总结!

至此,我的结果远不及 Karpathy 的好,可能有一下几个原因:

  1. 没有足够多的训练数据。
  2. 训练了一个小时之后我就没有耐心去查看 Colab 笔记本上的信息。
  3. Karpathy 使用了两层LSTM,包含了更多的参数,而我只使用了一层。
  4. 完全是另一回事。

但我得到了一些大致说得过去的结果!还不错!


via: https://jvns.ca/blog/2020/11/30/implement-char-rnn-in-pytorch/

作者:Julia Evans 选题:lujun9972 译者:zhangxiangping 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

拥有一致的命名标准是保持本地和上游 Git 仓库保持一致的关键。

当本地 Git 仓库的命名与远程仓库不一致时,与远程仓库协作就会变得很混乱。

解决此问题的一个方法是标准化两个词的使用和含义:origin 指的是你个人的 example.com/<USER>/* 仓库,而 upstream 指的是你从 origin 仓库 复刻 fork 出来的 example.com 仓库。换句话说,upstream 指的是公开提交工作的上游仓库,而 origin 指的是你对上游仓库的本地复刻,例如,你从这里生成 拉取请求 pull request (PR)。

pbench 仓库为例,下面是一个逐步建立新的本地克隆的方法,其中 originupstream 的定义是一致的。

1、在大多数 Git 托管服务上,当你想在上面工作时,必须对它进行复刻。当你运行自己的 Git 服务器时,这并不是必要的,但对于一个公开的代码库来说,这是一个在贡献者之间传输差异的简单方法。

创建一个 Git 仓库的复刻。在这个例子中,假设你的复刻位于 example.com/<USER>/pbench

2、接下来,你必须获得一个统一资源标识符 (URI),以便通过 SSH 进行 克隆 cloning 。在大多数 Git 托管服务上,比如 GitLab 或 GitHub,它在一个标有 “Clone” 或 “Clone over SSH” 的按钮或面板上,可以将克隆 URI 复制到剪贴板中。

3、在你的开发系统中,使用你复制的 URI 克隆仓库:

$ git clone [email protected]:<USER>/pbench.git

这将以默认名称 origin 来克隆 Git 仓库,作为你的 pbench 仓库复刻副本。

4、切换到刚才克隆的目录:

$ cd ~/pbench

5、下一步,获取源仓库的 SSH URI(你最初复刻的那个)。这可能和上面的方法一样。找到 “Clone” 按钮或面板,复制克隆地址。在软件开发中,这通常被称为“上游”,因为(理论上)这是大多数提交发生的地方,而你打算让这些提交流向下游的仓库。

6、将 URI 添加到你的本地仓库中。是的,将有两个不同的远程仓库分配给你的本地仓库副本:

$ git remote add upstream [email protected]:bigproject/pbench.git

7、现在你有两个命名远程仓库:originupstream。 你可以用 remote 子命令查看你的远程仓库:

$ git remote -v

现在,你的本地 master 分支正在跟踪 originmaster,这不一定是你想要的。你可能想跟踪这个分支的 upstream 版本,因为大多数开发都在上游进行。这个想法是,你要在从上游获得的内容的基础上添加更改。

8、将你的本地的 master 分支改成跟踪 upstream/master

$ git fetch upstream
$ git branch --set-upstream-to=upstream/master master

你可以对任何你想要的分支这样做,而不仅仅是 master。例如,有些项目使用 dev 分支来处理所有不稳定的变化,而将 master 保留给已批准发布的代码。

9、一旦你设置了你的跟踪分支,一定要变基(rebase)你的 master 分支,使它与上游仓库的任何新变化保持一致:

$ git remote update
$ git checkout master
$ git rebase

这是一个保持 Git 仓库在不同复刻之间同步的好方法。如果你想自动完成这项工作,请阅读 Seth Kenlon 关于使用 Ansible 托管 Git 仓库的文章。


via: https://opensource.com/article/20/11/multiple-git-repositories

作者:Peter Portante 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用别名为你最常用或复杂的 Git 命令创建快捷方式。

这篇出色的文章《改变我使用 Git 工作方式的七个技巧》启发了我写下另一个对我在命令行上使用 Git 的经验有重大影响的 Git 特性:别名。

定义 Git 的别名来替代命令有两大好处。

  • 它简化了有许多选项的长命令,使它们更短,更容易记住。
  • 缩短了经常使用的命令,使你的工作更有效率。

如何定义和使用别名

要定义 Git 的别名,请使用 git config 命令,加上别名和要替换的命令。例如,要为 git push 创建别名 p

$ git config --global alias.p 'push'

你可以通过将别名作为 git 的参数来使用别名,就像其他命令一样:

$ git p

要查看所有的别名,用 git config 列出你的配置:

$ git config --global -l
user.name=ricardo
[email protected]
alias.p=push

你也可以用你喜欢的 shell 来定义别名,比如 Bash 或 Zsh。不过,用 Git 定义别名有几个功能是用 shell 无法实现的。首先,它允许你在不同的 shell 中使用别名,而无需额外配置。此外,它还集成了 Git 的自动更正功能,所以当你输入错误的命令时,Git 可以建议你正确的别名。最后,Git 还会将别名保存在用户配置文件中,你可以通过复制一个文件将别名转移到其他机器上。

无论使用哪种方法,定义别名都能改善你使用 Git 的整体体验。更多关于定义 Git 别名的信息,请看《Git Book》。

8 个有用的 Git 别名

现在你知道如何创建和使用别名了,来看看一些有用的别名。

1、Git 状态

Git 命令行用户经常使用 status 命令来查看已更改或未跟踪的文件。默认情况下,这个命令提供了很多行的冗长输出,你可能不想要或不需要。你可以使用一个别名来处理这两个组件。定义别名 st 来缩短命令,并使用选项 -sb 来输出一个不那么啰嗦的状态和分支信息。

$ git config --global alias.st 'status -sb'

如果你在一个干净的分支上使用这个别名,你的输出就像这样:

$  git st
## master

在一个带有已更改和未跟踪文件的分支上使用它,会产生这样的输出:

$ git st
## master
 M test2
?? test3

2、Git 单行日志

创建一个别名,以单行方式显示你的提交,使输出更紧凑:

$ git config --global alias.ll 'log --oneline'

使用这个别名可以提供所有提交的简短列表:

$ git ll
33559c5 (HEAD -> master) Another commit
17646c1 test1

3、Git 的最近一次提交

这将显示你最近一次提交的详细信息。这是扩展了《Git Book》中 别名 一章的例子:

$ git config --global alias.last 'log -1 HEAD --stat'

用它来查看最后的提交:

$ git last
commit f3dddcbaabb928f84f45131ea5be88dcf0692783 (HEAD -> branch1)
Author: ricardo <[email protected]>
Date:   Tue Nov 3 00:19:52 2020 +0000

    Commit to branch1

 test2 | 1 +
 test3 | 0
 2 files changed, 1 insertion(+)

4、Git 提交

当你对 Git 仓库进行修改时,你会经常使用 git commit。使用 cm 别名使 git commit -m 命令更有效率:

$ git config --global alias.cm 'commit -m'

因为 Git 别名扩展了命令,所以你可以在执行过程中提供额外的参数:

$ git cm "A nice commit message"
[branch1 0baa729] A nice commit message
 1 file changed, 2 insertions(+)

5、Git 远程仓库

git remote -v 命令列出了所有配置的远程仓库。用别名 rv 将其缩短:

$ git config --global alias.rv 'remote -v'

6、Git 差异

git diff 命令可以显示不同提交的文件之间的差异,或者提交和工作树之间的差异。用 d 别名来简化它:

$ git config --global alias.d 'diff'

标准的 git diff 命令对小的改动很好用,但对于比较复杂的改动,外部工具如 vimdiff 就更有用。创建别名 dv 来使用 vimdiff 显示差异,并使用 y 参数跳过确认提示:

$ git config --global alias.dv 'difftool -t vimdiff -y'

使用这个别名来显示两个提交之间的 file1 差异:

$ git dv 33559c5 ca1494d file1

 title=

7、Git 配置列表

gl 别名可以更方便地列出所有用户配置:

$ git config --global alias.gl 'config --global -l'

现在你可以看到所有定义的别名(和其他配置选项):

$ git gl
user.name=ricardo
[email protected]
alias.p=push
alias.st=status -sb
alias.ll=log --oneline
alias.last=log -1 HEAD --stat
alias.cm=commit -m
alias.rv=remote -v
alias.d=diff
alias.dv=difftool -t vimdiff -y
alias.gl=config --global -l
alias.se=!git rev-list --all | xargs git grep -F

8、搜索提交

Git 别名允许你定义更复杂的别名,比如执行外部 shell 命令,可以在别名前加上 ! 字符。你可以用它来执行自定义脚本或更复杂的命令,包括 shell 管道。

例如,定义 se 别名来搜索你的提交:

$ git config --global alias.se '!git rev-list --all | xargs git grep -F'

使用这个别名来搜索提交中的特定字符串:

$ git se test2
0baa729c1d683201d0500b0e2f9c408df8f9a366:file1:test2
ca1494dd06633f08519ec43b57e25c30b1c78b32:file1:test2

自动更正你的别名

使用 Git 别名的一个很酷的好处是它与自动更正功能的原生集成。如果你犯了错误,默认情况下,Git 会建议使用与你输入的命令相似的命令,包括别名。例如,如果你把 status 打成了 ts,而不是 st,Git 会推荐正确的别名:

$ git ts
git: 'ts' is not a git command. See 'git --help'.

The most similar command is
        st

如果你启用了自动更正功能,Git 会自动执行正确的命令:

$ git config --global help.autocorrect 20
$ git ts
WARNING: You called a Git command named 'ts', which does not exist.
Continuing in 2.0 seconds, assuming that you meant 'st'.
## branch1
?? test4

优化 Git 命令

Git 别名是一个很有用的功能,它可以优化常见的重复性命令的执行,从而提高你的效率。Git 允许你定义任意数量的别名,有些用户会定义很多别名。我更喜欢只为最常用的命令定义别名 —— 定义太多别名会让人难以记忆,而且可能需要查找才能使用。

更多关于别名的内容,包括其他有用的内容,请参见 Git 维基的别名页面


via: https://opensource.com/article/20/11/git-aliases

作者:Ricardo Gerardi 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

序列化用于将数据转换为方便存储或传输的格式,然后将其重新构建以供使用。DRF 是最具有知名的序列化器。

序列化是将数据转换为可以存储或传输的格式,然后对其进行重新构建的过程。在开发应用程序或将数据存储在数据库、内存或将其转换为文件时,一直会用到它。

我最近帮助 Labcodes 的两名初级开发人员理解序列化器,我想也可以与诸位读者分享一下我的方法。

假设你正在编写一个电子商务网站,你有一个订单,该订单记录了某人在某个日期以某种价格购买了一个产品:

class Order:
    def __init__(self, product, customer, price, date):
        self.product = product
        self.customer = customer
        self.price = price
        self.date = date

现在,假设你想从一个键值数据库中存储和检索订单数据。幸运的是,它的接口可以接受和返回字典,因此你需要将对象转换成字典:

def serialize_order(order):
    return {
        'product': order.product,
        'customer': order.customer,
        'price': order.price,
        'date': order.date
    }

如果你想从数据库中获取一些数据,你可以获取字典数据并将其转换为订单对象(Order):

def deserialize_order(order_data):
    return Order(
        product=order_data['product'],
        customer=order_data['customer'],
        price=order_data['price'],
        date=order_data['date'],
    )

这对于简单的数据非常直接了当,但是当你需要处理一些由复杂属性构成的复杂对象时,这种方法就无法很好地扩展。你还需要处理不同类型字段的验证,这需要手工完成大量工作。

此时框架的序列化可以很方便的派上用场。它们使你可以创建带有少量模板的序列化器,这将适用于复杂的情况。

Django 提供了一个序列化模块,允许你将模型“转换”为其它格式:

from django.core import serializers

serializers.serialize('json', Order.objects.all())

它涵盖了 Web 应用程序最常用的种类,例如 JSON、YAML 和 XML。但是你也可以使用第三方序列化器或创建自己的序列化器。你只需要在 settings.py 文件中注册它:

# settings.py
SERIALIZATION_MODULES = {
    'my_format': appname.serializers.MyFormatSerializer,
}

要创建自己的 MyFormatSerializer,你需要实现 .serialize() 方法并接受一个查询集和其它选项作为参数:

class MyFormatSerializer:
    def serialize(self, queryset, **options):
        # serious serialization happening

现在,你可以将查询集序列化为新格式:

from django.core import serializers

serializers.serialize('my_format', Order.objects.all())

你可以使用选项参数来定义序列化程序的行为。例如,如果要定义在处理 ForeignKeys 时要使用嵌套序列化,或者只希望数据返回其主键,你可以传递一个 flat=True 参数作为选项,并在方法中处理:

class MyFormatSerializer:
    def serializer(self, queryset, **options):
        if options.get('flat', False):
            # don't recursively serialize relationships
        # recursively serialize relationships

使用 Django 序列化的一种方法是使用 loaddatadumpdata 管理命令。

DRF 序列化器

在 Django 社区中,Django REST 框架(DRF)提供了最著名的序列化器。尽管你可以使用 Django 的序列化器来构建将在 API 中响应的 JSON,但 REST 框架中的序列化器提供了更出色的功能,可以帮助你处理并轻松验证复杂的数据。

在订单的例子中,你可以像这样创建一个序列化器:

from restframework import serializers

class OrderSerializer(serializers.Serializer):
    product = serializers.CharField(max_length=255)
    customer = serializers.CharField(max_lenght=255)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    date = serializers.DateField()

轻松序列化其数据:

order = Order('pen', 'renato', 10.50, date.today())
serializer = OrderSerializer(order)

serializer.data
# {'product': 'pen', 'customer': 'renato', 'price': '10.50', 'date': '2020-08-16'}

为了能够从数据返回实例,你需要实现两个方法:createupdate

from rest_framework import serializers

class OrderSerializer(serializers.Serializer):
    product = serializers.CharField(max_length=255)
    customer = serializers.CharField(max_length=255)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    date = serializers.DateField()

    def create(self, validated_data):
        # 执行订单创建
        return order

    def update(self, instance, validated_data):
       # 执行实例更新
       return instance

之后,你可以通过调用 is_valid() 来验证数据,并通过调用 save() 来创建或更新实例:

serializer = OrderSerializer(**data)
## 若要验证数据,在调用 save 之前必须执行
serializer.is_valid()
serializer.save()

模型序列化器

序列化数据时,通常需要从数据库(即你创建的模型)进行数据处理。ModelSerializerModelForm 一样,提供了一个 API,用于从模型创建序列化器。假设你有一个订单模型:

from django.db import models

class Order(models.Model):
    product = models.CharField(max_length=255)
    customer = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    date = models.DateField()    

你可以像这样为它创建一个序列化器:

from rest_framework import serializers

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = '__all__'

Django 会自动在序列化器中包含所有模型字段,并创建 createudpate 方法。

在基于类的视图(CBV)中使用序列化器

像 Django CBV 中的 Forms 一样,序列化器可以很好地与 DRF 集成。你可以设置 serializer_class 属性,方便序列化器用于视图:

from rest_framework import generics

class OrderListCreateAPIView(generics.ListCreateAPIView):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

你也可以定义 get_serializer_class() 方法:

from rest_framework import generics

class OrderListCreateAPIView(generics.ListCreateAPIView):
    queryset = Order.objects.all()
   
    def get_serializer_class(self):
        if is_free_order():
            return FreeOrderSerializer
        return OrderSerializer

在 CBV 中还有其它与序列化器交互的方法。例如,get\_serializer() 返回一个已经实例化的序列化器,get\_serializer\_context() 返回创建实例时传递给序列化器的参数。对于创建或更新数据的视图,有 createupdate,它们使用 is_valid 方法验证数据,还有 perform\_createperform\_update 调用序列化器的 save 方法。

了解更多

要了解更多资源,参考我朋友 André Ericson 的经典 Django REST 框架网站。它是一个基于类的经典视图的 REST 框架版本,可让你深入查看组成 DRF 的类。当然,官方文档也是一个很棒的资源。


via: https://opensource.com/article/20/11/django-rest-framework-serializers

作者:Renato Oliveira 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出