2019年5月

及时备份很重要。即使在 Fedora Magazine 中,备份软件 也是一个常见的讨论话题。本文演示了如何仅使用 systemd 以及 restic 来自动备份。

有关 restic 的介绍,请查看我们的文章在 Fedora 上使用 restic 进行加密备份。然后继续阅读以了解更多详情。

为了自动创建快照以及清理数据,需要运行两个 systemd 服务。第一个运行备份命令的服务需要以常规频率运行。第二个服务负责数据清理。

如果你根本不熟悉 systemd,那么这是个很好的学习机会。查看 Magazine 上关于 systemd 的系列文章,从单元文件的这个入门开始:

如果你还没有安装 restic,请注意它在官方的 Fedora 仓库中。要安装它,请带上 sudo 运行此命令:

$ sudo dnf install restic

备份

首先,创建 ~/.config/systemd/user/restic-backup.service。将下面的文本复制并粘贴到文件中以获得最佳效果。

[Unit]
Description=Restic backup service
[Service]
Type=oneshot
ExecStart=restic backup --verbose --one-file-system --tag systemd.timer $BACKUP_EXCLUDES $BACKUP_PATHS
ExecStartPost=restic forget --verbose --tag systemd.timer --group-by "paths,tags" --keep-daily $RETENTION_DAYS --keep-weekly $RETENTION_WEEKS --keep-monthly $RETENTION_MONTHS --keep-yearly $RETENTION_YEARS
EnvironmentFile=%h/.config/restic-backup.conf

此服务引用环境文件来加载密钥(例如 RESTIC_PASSWORD)。创建 ~/.config/restic-backup.conf。复制并粘贴以下内容以获得最佳效果。此示例使用 BackBlaze B2 存储。请相应地调整 ID、密钥、仓库和密码值。

BACKUP_PATHS="/home/rupert"
BACKUP_EXCLUDES="--exclude-file /home/rupert/.restic_excludes --exclude-if-present .exclude_from_backup"
RETENTION_DAYS=7
RETENTION_WEEKS=4
RETENTION_MONTHS=6
RETENTION_YEARS=3
B2_ACCOUNT_ID=XXXXXXXXXXXXXXXXXXXXXXXXX
B2_ACCOUNT_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
RESTIC_REPOSITORY=b2:XXXXXXXXXXXXXXXXXX:/
RESTIC_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

现在已安装该服务,请重新加载 systemd:systemctl -user daemon-reload。尝试手动运行该服务以创建备份:systemctl -user start restic-backup

因为该服务类型是一次性,它将运行一次并退出。验证服务运行并根据需要创建快照后,设置计时器以定期运行此服务。例如,要每天运行 restic-backup.service,请按如下所示创建 ~/.config/systemd/user/restic-backup.timer。再次复制并粘贴此文本:

[Unit]
Description=Backup with restic daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target

运行以下命令启用:

$ systemctl --user enable --now restic-backup.timer

清理

虽然主服务运行 forget 命令仅保留保留策略中的快照,但实际上并未从 restic 仓库中删除数据。 prune 命令检查仓库和当前快照,并删除与快照无关的所有数据。由于 prune 可能是一个耗时的过程,因此无需在每次运行备份时运行。这是第二个服务和计时器的场景。首先,通过复制和粘贴此文本来创建文件 ~/.config/systemd/user/restic-prune.service

[Unit]
Description=Restic backup service (data pruning)
[Service]
Type=oneshot
ExecStart=restic prune
EnvironmentFile=%h/.config/restic-backup.conf

与主 restic-backup.service 服务类似,restic-prune 也是一次性服务,并且可以手动运行。设置完服务后,创建 ~/.config/systemd/user/restic-prune.timer 并启用相应的计时器:

[Unit]
Description=Prune data from the restic repository monthly
[Timer]
OnCalendar=monthly
Persistent=true
[Install]
WantedBy=timers.target

就是这些了!restic 将会每日运行并按月清理数据。


图片来自 UnsplashSamuel Zeller 拍摄。


via: https://fedoramagazine.org/automate-backups-with-restic-and-systemd/

作者:Link Dupont 选题:lujun9972 译者:geekpi 校对:wxy

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

在本系列的第四部分,学习如何编写移动游戏角色的控制代码。

在这个系列的第一篇文章中,我解释了如何使用 Python 创建一个简单的基于文本的骰子游戏。在第二部分中,我向你们展示了如何从头开始构建游戏,即从 创建游戏的环境 开始。然后在第三部分,我们创建了一个玩家妖精,并且使它在你的(而不是空的)游戏世界内生成。你可能已经注意到,如果你不能移动你的角色,那么游戏不是那么有趣。在本篇文章中,我们将使用 Pygame 来添加键盘控制,如此一来你就可以控制你的角色的移动。

在 Pygame 中有许多函数可以用来添加(除键盘外的)其他控制,但如果你正在敲击 Python 代码,那么你一定是有一个键盘的,这将成为我们接下来会使用的控制方式。一旦你理解了键盘控制,你可以自己去探索其他选项。

在本系列的第二篇文章中,你已经为退出游戏创建了一个按键,移动角色的(按键)原则也是相同的。但是,使你的角色移动起来要稍微复杂一点。

让我们从简单的部分入手:设置控制器按键。

为控制你的玩家妖精设置按键

在 IDLE、Ninja-IDE 或文本编辑器中打开你的 Python 游戏脚本。

因为游戏需要时刻“监听”键盘事件,所以你写的代码需要连续运行。你知道应该把需要在游戏周期中持续运行的代码放在哪里吗?

如果你回答“放在主循环中”,那么你是正确的!记住除非代码在循环中,否则(大多数情况下)它只会运行仅一次。如果它被写在一个从未被使用的类或函数中,它可能根本不会运行。

要使 Python 监听传入的按键,将如下代码添加到主循环。目前的代码还不能产生任何的效果,所以使用 print 语句来表示成功的信号。这是一种常见的调试技术。

while main == True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); sys.exit()
            main = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                print('left')
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                print('right')
            if event.key == pygame.K_UP or event.key == ord('w'):
                print('jump')

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                print('left stop')
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                print('right stop')
            if event.key == ord('q'):
                pygame.quit()
                sys.exit()
                main = False    

一些人偏好使用键盘字母 WASD 来控制玩家角色,而另一些偏好使用方向键。因此确保你包含了两种选项。

注意:当你在编程时,同时考虑所有用户是非常重要的。如果你写代码只是为了自己运行,那么很可能你会成为你写的程序的唯一用户。更重要的是,如果你想找一个通过写代码赚钱的工作,你写的代码就应该让所有人都能运行。给你的用户选择权,比如提供使用方向键或 WASD 的选项,是一个优秀程序员的标志。

使用 Python 启动你的游戏,并在你按下“上下左右”方向键或 ADW 键的时候查看控制台窗口的输出。

$ python ./your-name_game.py
  left
  left stop
  right
  right stop
  jump

这验证了 Pygame 可以正确地检测按键。现在是时候来完成使妖精移动的艰巨任务了。

编写玩家移动函数

为了使你的妖精移动起来,你必须为你的妖精创建一个属性代表移动。当你的妖精没有在移动时,这个变量被设为 0

如果你正在为你的妖精设置动画,或者你决定在将来为它设置动画,你还必须跟踪帧来使走路循环保持在轨迹上。

Player 类中创建如下变量。开头两行作为上下文对照(如果你一直跟着做,你的代码中就已经有这两行),因此只需要添加最后三行:

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.movex = 0 # 沿 X 方向移动
        self.movey = 0 # 沿 Y 方向移动
        self.frame = 0 # 帧计数

设置好了这些变量,是时候去为妖精移动编写代码了。

玩家妖精不需要时刻响应控制,有时它并没有在移动。控制妖精的代码,仅仅只是玩家妖精所有能做的事情中的一小部分。在 Python 中,当你想要使一个对象做某件事并独立于剩余其他代码时,你可以将你的新代码放入一个函数。Python 的函数以关键词 def 开头,(该关键词)代表了定义函数。

在你的 Player 类中创建如下函数,来为你的妖精在屏幕上的位置增加几个像素。现在先不要担心你增加几个像素,这将在后续的代码中确定。

    def control(self,x,y):
        '''
        控制玩家移动
        '''
        self.movex += x
        self.movey += y

为了在 Pygame 中移动妖精,你需要告诉 Python 在新的位置重绘妖精,以及这个新位置在哪里。

因为玩家妖精并不总是在移动,所以更新只需要是 Player 类中的一个函数。将此函数添加前面创建的 control 函数之后。

要使妖精看起来像是在行走(或者飞行,或是你的妖精应该做的任何事),你需要在按下适当的键时改变它在屏幕上的位置。要让它在屏幕上移动,你需要将它的位置(由 self.rect.xself.rect.y 属性指定)重新定义为当前位置加上已应用的任意 movexmovey。(移动的像素数量将在后续进行设置。)

    def update(self):
        '''
        更新妖精位置
        '''
        self.rect.x = self.rect.x + self.movex        

对 Y 方向做同样的处理:

        self.rect.y = self.rect.y + self.movey

对于动画,在妖精移动时推进动画帧,并使用相应的动画帧作为玩家的图像:

        # 向左移动
        if self.movex < 0:
            self.frame += 1
            if self.frame > 3*ani:
                self.frame = 0
            self.image = self.images[self.frame//ani]

        # 向右移动
        if self.movex > 0:
            self.frame += 1
            if self.frame > 3*ani:
                self.frame = 0
            self.image = self.images[(self.frame//ani)+4]

通过设置一个变量来告诉代码为你的妖精位置增加多少像素,然后在触发你的玩家妖精的函数时使用这个变量。

首先,在你的设置部分创建这个变量。在如下代码中,开头两行是上下文对照,因此只需要在你的脚本中增加第三行代码:

player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10  # 移动多少个像素

现在你已经有了适当的函数和变量,使用你的按键来触发函数并将变量传递给你的妖精。

为此,将主循环中的 print 语句替换为玩家妖精的名字(player)、函数(.control)以及你希望玩家妖精在每个循环中沿 X 轴和 Y 轴移动的步数。

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(-steps,0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(steps,0)
            if event.key == pygame.K_UP or event.key == ord('w'):
                print('jump')

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps,0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps,0)
            if event.key == ord('q'):
                pygame.quit()
                sys.exit()
                main = False

记住,steps 变量代表了当一个按键被按下时,你的妖精会移动多少个像素。如果当你按下 D 或右方向键时,你的妖精的位置增加了 10 个像素。那么当你停止按下这个键时,你必须(将 step)减 10(-steps)来使你的妖精的动量回到 0。

现在尝试你的游戏。注意:它不会像你预想的那样运行。

为什么你的妖精仍无法移动?因为主循环还没有调用 update 函数。

将如下代码加入到你的主循环中来告诉 Python 更新你的玩家妖精的位置。增加带注释的那行:

    player.update()  # 更新玩家位置
    player_list.draw(world)
    pygame.display.flip()
    clock.tick(fps)

再次启动你的游戏来见证你的玩家妖精在你的命令下在屏幕上来回移动。现在还没有垂直方向的移动,因为这部分函数会被重力控制,不过这是另一篇文章中的课程了。

与此同时,如果你拥有一个摇杆,你可以尝试阅读 Pygame 中 joystick 模块相关的文档,看看你是否能通过这种方式让你的妖精移动起来。或者,看看你是否能通过鼠标与你的妖精互动。

最重要的是,玩的开心!

本教程中用到的所有代码

为了方便查阅,以下是目前本系列文章用到的所有代码。

#!/usr/bin/env python3
# 绘制世界
# 添加玩家和玩家控制
# 添加玩家移动控制

# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

import pygame
import sys
import os

'''
Objects
'''

class Player(pygame.sprite.Sprite):
    '''
    生成玩家
    '''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.movex = 0
        self.movey = 0
        self.frame = 0
        self.images = []
        for i in range(1,5):
            img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
            img.convert_alpha()
            img.set_colorkey(ALPHA)
            self.images.append(img)
            self.image = self.images[0]
            self.rect  = self.image.get_rect()

    def control(self,x,y):
        '''
        控制玩家移动
        '''
        self.movex += x
        self.movey += y

    def update(self):
        '''
        更新妖精位置
        '''

        self.rect.x = self.rect.x + self.movex
        self.rect.y = self.rect.y + self.movey

        # 向左移动
        if self.movex < 0:
            self.frame += 1
            if self.frame > 3*ani:
                self.frame = 0
            self.image = self.images[self.frame//ani]

        # 向右移动
        if self.movex > 0:
            self.frame += 1
            if self.frame > 3*ani:
                self.frame = 0
            self.image = self.images[(self.frame//ani)+4]


'''
设置
'''
worldx = 960
worldy = 720

fps = 40        # 帧刷新率
ani = 4        # 动画循环
clock = pygame.time.Clock()
pygame.init()
main = True

BLUE  = (25,25,200)
BLACK = (23,23,23 )
WHITE = (254,254,254)
ALPHA = (0,255,0)

world = pygame.display.set_mode([worldx,worldy])
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
backdropbox = world.get_rect()
player = Player()   # 生成玩家
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10      # 移动速度

'''
主循环
'''
while main == True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); sys.exit()
            main = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(-steps,0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(steps,0)
            if event.key == pygame.K_UP or event.key == ord('w'):
                print('jump')

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps,0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps,0)
            if event.key == ord('q'):
                pygame.quit()
                sys.exit()
                main = False

#    world.fill(BLACK)
    world.blit(backdrop, backdropbox)
    player.update()
    player_list.draw(world) # 更新玩家位置
    pygame.display.flip()
    clock.tick(fps)

你已经学了很多,但还仍有许多可以做。在接下来的几篇文章中,你将实现添加敌方妖精、模拟重力等等。与此同时,练习 Python 吧!


via: https://opensource.com/article/17/12/game-python-moving-player

作者:Seth Kenlon 选题:lujun9972 译者:cycoe 校对:wxy

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

Stack Overflow 遭黑客入侵一事仍在调查中,官方博客披露了调查最新进展。入侵实际发生在 5 月 5 日,当时部署到 stackoverflow.com 的开发层的构建包含一个错误,该错误允许攻击者登录到开发层,并在网站的生产版本上升级他们的访问权限。

黑客潜入系统并探索了至少 5 天都未被发现,直到 5 月 11 日,“入侵者对我们的系统进行了更改,以便为自己提供访问特权。这一变化很快被发现,我们撤销了他们在整个网络的访问,开始调查入侵,并采取修复措施。”

调查显示整体用户数据库没有受到损害,攻击者提出的特权 Web 请求已经确定,这些请求返回了约 250 位 Stack Exchange 用户的 IP 地址、名称或电子邮件。受影响用户将很快接到官方的通知。

来源:开源中国

更多资讯

美官员警告 5G 网络或对天气预报系统造成严重破坏

几个月来,美国宇航局(NASA)国家海洋和大气管理局(NOAA)一直在向联邦通信委员会(FCC)发出警告,称之拍卖的 5G 无线网络频谱,可能对天气预报系统的数据收集系统造成严重的影响,从而影响飓风预测的准确性。周四的时候,NOAA 代理主席 Neil Jacobs 在国会山表示,正在推进的 5G 网络建设,或将天气预报的准确性降低 30% 。

来源: cnBeta.COM

详情: http://t.cn/EKeVLaK

苹果设备被曝存在 PEAP 认证漏洞 研究人员对官方修复方案存疑

研究人员发现苹果设备在 PEAP 认证上存在缺陷,攻击者可强迫苹果设备接入恶意热点。研究人员称,即使身份验证服务器(RADIUS)也不能证明密码的真实性,该错误依然允许攻击者强制任何 Apple 设备(iOS、macOS 或 tvOS)与恶意接入点关联。

来源: 雷锋网

详情: http://t.cn/EKeV4kM

出来混迟早要还 FBI 通缉 5 名在逃黑客

GozNym 这个词,想必不少人的脑海中还有印象——2016 年,这个屌炸天的黑客“天团”用 GozNym 恶意程序攻击了 24 家位于美国及加拿大的银行,只花了短短的几天便盗走数百万美元。时隔三年,他们过得咋样呢?没错,全球通缉中。至于上面那个笑话嘛,一半是真事,另一半即将成真。

来源: 雷锋网
详情: http://t.cn/EKeVVfM

俄罗斯黑客又开始行动 这次目标是儿童电视选秀节目

听到俄罗斯黑客时,我们通常会想到他们对美国选举干预行为,但他们确实有其他用途,包括为儿童操纵俄罗斯电视选秀节目。在俄罗斯一次电视选秀节目当中,11 岁的米凯拉·阿布拉莫娃在获得 56.5% 的选票,赢得了 3 万张选票,荣膺“俄罗斯声音之子”的桂冠。

来源: cnBeta.COM

详情: http://t.cn/EKeVa0p

(信息来源于网络,安华金和搜集整理)

在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用 GNOME 桌面的发行版中添加应用图标。

一个经典的桌面操作系统在“桌面屏”上总是有图标的。这些桌面图标包括文件管理器、回收站和应用图标。

当在 Windows 中安装应用时,一些程序会询问你是否在桌面创建一个快捷方式。但在 Linux 系统中不是这样。

但是如果你热衷于这个特点,让我给你展示如何在 Ubuntu 桌面和其他使用 GNOME 桌面的发行版中创建应用的快捷方式。

Application Shortcuts on Desktop in Ubuntu with GNOME desktop

如果你想知道我的桌面外观,我正在使用 Ant 主题和 Tela 图标集。你可以获取一些 GTK 主题为 Ubuntu 准备的图标集并换成你喜欢的。

在 Ubuntu 中添加桌面快捷方式

个人来讲,我更喜欢为应用图标准备的 Ubuntu 启动器方式。如果我经常使用一个程序,我会添加到启动器。但是我知道不是每个人都有相同的偏好,可能少数人更喜欢桌面的快捷方式。

让我们看在桌面中创建应用快捷方式的最简单方式。

免责声明

这篇指南已经在 Ubuntu 18.04 LTS 的 GNOME 桌面上测试过了。它可能在其他发行版和桌面环境上也能发挥作用,但你必须自己尝试。一些 GNOME 特定步骤可能会变,所以请在其他桌面环境尝试时注意。

准备

首先最重要的事是确保你有 GNOME 桌面的图标权限。

如果你跟随 Ubuntu 18.04 自定义提示,你会知道如何安装 GNOME Tweaks 工具。在这个工具中,确保你设置“Show Icons”选项为启用。

Allow icons on desktop in GNOME

一旦你确保已经设置,是时候在桌面添加应用快捷方式了。

第一步:定位应用的 .desktop 文件

到 “Files -> Other Location -> Computer”。

Go to Other Locations -&gt; Computer

从这里,到目录 “usr -> share -> applications”。你会在这里看到几个你已经安装的 Ubuntu 应用。即使你没有看到图标,你应该看到被命名为“应用名.desktop”形式的文件。

Application Shortcuts

第二步:拷贝 .desktop 文件到桌面

现在你要做的只是查找应用图标(或者它的 desktop 文件)。当你找到后,拖文件到桌面或者拷贝文件(使用 Ctrl+C 快捷方式)并在桌面粘贴(使用 Ctrl+V 快捷方式)。

Add .desktop file to the desktop

第三步:运行 desktop 文件

当你这么做,你应该在桌面上看到一个图标的文本文件而不是应用 logo。别担心,一会就不一样了。

你要做的就是双击桌面的那个文件。它将警告你它是一个“未信任的应用启动器’,点击“信任并启动”。

Launch Desktop Shortcut

这个应用像往常一样启动,好事是你会察觉到 .desktop 文件现在已经变成应用图标了。我相信你喜欢应用图标的方式,不是吗?

Application shortcut on the desktop

Ubuntu 19.04 或者 GNOME 3.32 用户的疑难杂症

如果你使用 Ubuntu 19.04 或者 GNOME 3.32,你的 .desktop 文件可能根本不会启动。你应该右击 .desktop 文件并选择 “允许启动”。

在这之后,你应该能够启动应用并且桌面上的应用快捷方式能够正常显示了。

总结

如果你不喜欢桌面的某个应用启动器,选择删除就是了。它会删除应用快捷方式,但是应用仍安全的保留在你的系统中。

我希望你发现这篇快速指南有帮助并喜欢在 Ubuntu 桌面上的应用快捷方式。

如果你有问题或建议,请在下方评论让我知道。


via: https://itsfoss.com/ubuntu-desktop-shortcut/

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

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

在我们覆盖 7 个 PyPI 库的系列文章中了解更多解决 Python 问题的信息。

Python是当今使用最多流行的编程语言之一,因为:它是开源的,它具有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区是我们在 Python Package Index(PyPI)中提供如此庞大、多样化的软件包的原因,用以扩展和改进 Python。并解决不可避免的问题。

在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。今天,我们将研究 attrs,这是一个帮助你快速编写简洁、正确的代码的 Python 包。

attrs

如果你已经写过一段时间的 Python,那么你可能习惯这样写代码:

class Book(object):

    def __init__(self, isbn, name, author):
        self.isbn = isbn
        self.name = name
        self.author = author

接着写一个 __repr__ 函数。否则,很难记录 Book 的实例:

def __repr__(self):
    return f"Book({self.isbn}, {self.name}, {self.author})"

接下来你会写一个好看的 docstring 来记录期望的类型。但是你注意到你忘了添加 editionpublished_year 属性,所以你必须在五个地方修改它们。

如果你不必这么做如何?

@attr.s(auto_attribs=True)
class Book(object):
    isbn: str
    name: str
    author: str
    published_year: int
    edition: int

使用新的类型注释语法注释类型属性,attrs 会检测注释并创建一个类。

ISBN 有特定格式。如果我们想强行使用该格式怎么办?

@attr.s(auto_attribs=True)
class Book(object):
    isbn: str = attr.ib()
    @isbn.validator
    def pattern_match(self, attribute, value):
        m = re.match(r"^(\d{3}-)\d{1,3}-\d{2,3}-\d{1,7}-\d$", value)
        if not m:
            raise ValueError("incorrect format for isbn", value)
    name: str 
    author: str
    published_year: int
    edition: int

attrs 库也对不可变式编程支持良好。将第一行改成 @attr.s(auto_attribs=True, frozen=True) 意味着 Book 现在是不可变的:尝试修改一个属性将会引发一个异常。相反,比如,如果希望将发布日期向后一年,我们可以修改成 attr.evolve(old_book, published_year=old_book.published_year+1) 来得到一个新的实例。

本系列的下一篇文章我们将来看下 singledispatch,一个能让你向 Python 库添加方法的库。

查看本系列先前的文章:


via: https://opensource.com/article/19/5/python-attrs

作者:Moshe Zadka 选题:lujun9972 译者:geekpi 校对:wxy

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

你还记得篇流行的博客文章《PHP:设计糟糕的分形》吗?我第一次读到它时,我在一个有很多遗留的 PHP 项目的糟糕地方工作。这篇文章让我觉得我是否应该放弃,并去做与编程完全不同的事情。

还好,我之后很快就换了工作,更重要的是,自从 5.x 版本以来,PHP 成功地进步了很多。今天,我在向那些不再使用 PHP 编程,或者陷入遗留项目的人们致意。

剧透:今天有些事情仍然很糟糕,就像几乎每种编程语言都有它的怪癖一样。许多核心功能仍然有不一致的调用方法,仍然有令人困惑的配置设置,仍然有许多开发人员在那里写蹩脚的代码 —— 因为他们必须如此,或是他们不知道更好的写法。

今天我想看看好的一面:让我们关注已经发生变化的事情,以及编写干净而可维护的 PHP 代码的方法。在此之前,我想请你暂时搁置任何偏见。

然后,你可以像以前一样对 PHP 自由吐槽。虽然,你可能会对 PHP 在过去的几年里的一些改进感到惊讶。(LCTT 译注:说实话,我是真的感到吃惊)

提前看结论

  • PHP 在积极地开发,每年都有新版本
  • 自 PHP 5 时代以来的性能已经翻倍,如果不是三倍的话
  • 有一个非常活跃的框架、包和平台的生态系统
  • PHP 在过去几年中添加了许多新功能,并且这种语言在不断发展
  • 像静态分析这样的工具在过去几年中已经成熟,并且一直保持增长 更新:人们让我展示一些实际的代码。我觉得这没问题!这是我的一个业余项目的源代码,用 PHP 和 Laravel 编写的;这里列出了我们在办公室维护的几百个自由开源软件包。这两者都是现代 PHP 项目的好例子。

那让我们开始吧。

历史总结

出于更好地衡量的目的,让我们快速回顾一下如今的 PHP 发布周期。我们现在的 PHP 为 7.3,预计在 2019 年底为 7.4。PHP 8.0 将是 7.4 之后的下一个版本。

自从 5.x 时代以来,核心团队试图保持每年发布一个版本的周期,并且他们在过去的四年中成功地做到了这一点。

一般来说,每个新版本都会在两年内得到积极支持,并再获得一年以上的“安全修复”。其目标是激励 PHP 开发人员尽可能保持最新:例如,每年进行小规模升级比在 5.4 到 7.0 之间跳转更容易。

可以在 这里 找到 PHP 时间轴的活动概述。

最后,PHP 5.6 是最新的 5.x 版本,而 8.0 是当前的下一个大版本。如果你想知道 PHP 6 发生了什么,你可以听听 PHP Roundtable 播客

了解了这个,让我们揭穿一些关于现代 PHP 的常见误解。

PHP 的性能

早在 5.x 时代,PHP 的表现就是……嗯,平均水平。但是在 7.0 版本中,PHP 从头开始重写了核心部分,导致其性能提升了两到三倍!

但光是嘴说是不够的。让我们来看看基准测试。幸运的是,人们花了很多时间对 PHP 性能进行了基准测试。 我发现 Kinsta 有一个很好的更新的测试列表。

自 7.0 升级以来,性能就一直在提升而没有回退。PHP Web 应用程序的性能可与其它语言中的 Web 框架相提并论,甚至在某些情况下更好。你可以看看这个广泛的基准测试套件

当然 PHP 框架不会胜过 C 和 Rust,但它们比 Rails 或 Django 要好得多,并且与 ExpressJS 相当。

框架和生态系统

说到框架:PHP 可不仅仅是 WordPress。让我告诉你 —— 某些专业的 PHP 开发人员:WordPress 绝不代表当代的 PHP 生态系统。

一般来说,有两个主要的 Web 应用程序框架,SymfonyLaravel,以及一些较小的应用程序框架。当然还有 Zend、Yii、Cake、Code Igniter 等等,但是如果你想知道现代 PHP 开发是怎么样的,这两者之一都是很好的选择。

这两个框架都有一个庞大的包和产品的生态系统。从管理面板和 CRM 到独立软件包,从 CI 到分析器,以及几个 Web 套接字服务器、队列管理器、支付集成等众多服务。老实说,要列出的内容太多了。

这些框架虽然适用于实际开发。如果你只是需要个内容管理系统(CMS),WordPress 和 CraftCMS 等平台就够了。

衡量 PHP 生态系统当前状态的一种方法是查看 Packagist,这是 PHP 主要的软件包存储库。它现在呈指数级增长。每天下载量达到了 2500 万次,可以说 PHP 生态系统已不再是以前的小型弱势群体了。

请查看此图表,它列出一段时间内的软件包和版本数量变化。它也可以在 Packagist 网站上找到它。

除了应用程序框架和 CMS 之外,我们还看到过去几年里异步框架的兴起。

这些是用 PHP 或其他语言编写的框架和服务器,允许用户运行真正的异步 PHP,这些例子包括 Swoole(创始人韩天峰),以及 AmpReactPHP

我们已经进入了异步的世界,像 Web 套接字和具有大量 I/O 的应用程序之类的东西在 PHP 世界中已经变得非常重要。

在内部邮件列表里(PHP 核心开发人员讨论语言开发的地方)已经谈到了将 libuv 添加到核心。如果你还不知道 libuv:Node.js 全有赖它提供异步性。

语言本身

虽然尚未提供 asyncawait,但在过去几年中,PHP 语言本身已经有了很多改进。这是 PHP 中新功能的非详尽列表:

当我们讨论语言功能时,我们还要谈谈当今该语言的发展过程。虽然社区可以提出 RFC,但是得有一个活跃的志愿者核心团队才能推着它前进。

接下来,这些 RFC 将在“内部”邮件列表中进行讨论,这个邮件列表也可以在线阅读。在添加新的语言特性之前,必须进行投票。只有得到了至少 2/3 多数同意的 RFC 才能进入核心。

可能有大约 100 人能够投票,但不需要每个人对每个 RFC 进行投票。核心团队的成员当然可以投票,他们是维护代码库的人。除了他们之外,还有一群人从 PHP 社区中被单独挑选出来。这些人包括 PHP 文档的维护者,对 PHP 项目整体有贡献的人,以及 PHP 社区中的杰出开发人员。

虽然大多数核心开发都是在自愿的基础上完成的,但其中一位核心 PHP 开发人员 Nikita Popov 最近受雇于 JetBrains 全职从事于 PHP 语言的开发。另一个例子是 Linux 基金会最近决定投资 Zend 框架。像这样的雇佣和收购确保了 PHP 未来发展的稳定性。

工具

除了核心本身,我们看到过去几年中围绕它的工具有所增加。首先浮现于我脑海中的是静态分析器,比如由 Vimeo 创建 Psalm,以及 PhanPHPStan

这些工具将静态分析你的 PHP 代码并报告任何类型错误和可能的错误等。在某种程度上,它们提供的功能可以与 TypeScript 进行比较,但是现在这种语言不能 转译 transpiling ,因此不支持使用自定义语法。

尽管这意味着我们需要依赖 docblocks,但是 PHP 之父 Rasmus Lerdorf 确实提到了添加静态分析引擎到核心的想法。虽然会有很多潜力,但这是一项艰巨的任务。

说到转译,以及受到 JavaScript 社区的启发;他们已经努力在用户领域中扩展 PHP 语法。一个名为 Pre 的项目正是如此:允许将新的 PHP 语法转译为普通的 PHP 代码。

虽然这个思路已经在 JavaScript 世界中被证明了,但如果提供了适当的 IDE 和静态分析支持,它就能在 PHP 中工作了。这是一个非常有趣的想法,但必须发展起来才能称之为“主流”。

结语

尽管如此,你仍然可以将 PHP 视为一种糟糕的语言。虽然这种语言肯定有它的缺点和背负了 20 年的遗产;但我可以放胆地说,我喜欢用它工作。

根据我的经验,我能够创建可靠、可维护和高质量的软件。我工作的客户对最终结果感到满意,“俺也一样”。

尽管仍然可以用 PHP 做很多乱七八糟的事情,但我认为如果明智和正确地使用的话,它是 Web 开发的绝佳选择。

你不同意吗?让我知道为什么!你可以通过 Twitter电子邮件 与我联系。


via: https://stitcher.io/blog/php-in-2019

作者:Brent 选题:lujun9972 译者:wxy 校对:wxy

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