From 8a951f6c7eeb642f6de9dfe2ac9e5bc121b049fb Mon Sep 17 00:00:00 2001 From: Bestony Date: Wed, 7 Sep 2016 11:02:59 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E5=BC=80=E5=A7=8B=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 已和原译者ynmlml沟通 --- .../20160620 5 Best Linux Package Managers for Linux Newbies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md b/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md index a92c5265e5..caac94b3bc 100644 --- a/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md +++ b/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md @@ -1,4 +1,4 @@ -transalting by ynmlml +transalting by bestony 5 Best Linux Package Managers for Linux Newbies ===================================================== From a8c40380f4eaff1a30ada0ae19c1ac4a7bec0d12 Mon Sep 17 00:00:00 2001 From: Ezio Date: Wed, 7 Sep 2016 17:28:45 +0800 Subject: [PATCH 2/8] =?UTF-8?q?20160907-1=20=E9=80=89=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Project Documentation with GitHub Pages.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 sources/tech/20160823 Publish Your Project Documentation with GitHub Pages.md diff --git a/sources/tech/20160823 Publish Your Project Documentation with GitHub Pages.md b/sources/tech/20160823 Publish Your Project Documentation with GitHub Pages.md new file mode 100644 index 0000000000..ff8cd2385e --- /dev/null +++ b/sources/tech/20160823 Publish Your Project Documentation with GitHub Pages.md @@ -0,0 +1,49 @@ +Publish Your Project Documentation with GitHub Pages +===== + +You might be familiar with [how GitHub Pages helps you share your work with the world][3] or maybe you have [attended a class][4] that helped you build your first GitHub Pages site. Recent improvements to GitHub Pages have made it easier to [publish your site from a variety of sources][5]. One of these sources is your repository's /docs folder. + +Quality documentation is a hallmark of any healthy software project. For open-source projects, however, maintaining a robust compendium of knowledge detailing all the ins and outs is paramount. Well-curated documentation increases your project's approachability, provides asynchronous guidance, and fosters the type of uncoordinated collaboration that propels open-source software development. + +Hosting your documentation on the web can present time-consuming challenges that make publishing and maintaining it an unrewarding experience — one that it's often easy to avoid. Grappling with multiple disparate publishing tools like FTP servers and databases means files often exist in various states and multiple locations, all of which require manual synchronization. To be clear, conventional web publishing provides unparalleled flexibility and power; but it comes at the expense of simplicity and, in many cases, utility. + +When it comes to documentation, a path with less resistance is often the better approach. + +[GitHub Pages][2] gives you a direct path to create websites for your projects, which makes it a natural choice for publishing and maintaining documentation. Because GitHub Pages supports Jekyll, you can pen your documentation in plain text or Markdown to help maintain a lower barrier to contribution. Jekyll also includes support for many helpful tools like variables, templates, and automatic code highlighting, which gives you much of the flexibility you'd find in a bulkier platform without the added complexity. + +Most importantly, using GitHub Pages means your documentation lives alongside your code on GitHub, where you can use things like Issues and Pull Requests to ensure it receives the high level of care it deserves; and because GitHub Pages lets you publish from the /docs directory on the master branch, you can maintain your codebase and its published documentation on the same branch. + +### Get started today + +Publishing your first documentation page only takes a few minutes. + +1. Create a /docs/index.md file on your repository's master branch. + +2. Add your content and any necessary Jekyll front matter, then commit your changes. + +![](https://cloud.githubusercontent.com/assets/3477155/17778793/47c5a586-6533-11e6-982c-ebd41ec6968c.gif) + +1. Visit your repository's settings tab and select master branch /docs folder as the GitHub Pages source. Click save, and you're done. + +![](https://cloud.githubusercontent.com/assets/3477155/17778792/47c2ecc4-6533-11e6-828a-91980daa7297.gif) + +GitHub Pages will read the contents of your /docs directory, convert the index.md into HTML, and publish the results at your GitHub Pages URL. + +This will generate the most basic HTML output that you can further customize with templates, CSS, and other features available in Jekyll. To see examples of what all is possible, take a look at the [GitHub Pages Showcase][1]. + +-------------------------------------------------------------------------------- + +via: https://github.com/blog/2233-publish-your-project-documentation-with-github-pages + +作者:[ loranallensmith ][a] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://github.com/loranallensmith +[1]: https://github.com/showcases/github-pages-examples +[2]: https://pages.github.com/ +[3]: https://www.youtube.com/watch?v=2MsN8gpT6jY +[4]: https://www.youtube.com/watch?v=RaKX4A5EiQo +[5]: https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/ From 131f1dc054ba5efa6e8cd82cd8fc28bfede8613e Mon Sep 17 00:00:00 2001 From: Ezio Date: Wed, 7 Sep 2016 17:36:13 +0800 Subject: [PATCH 3/8] =?UTF-8?q?20160907-2=20=E9=80=89=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0160817 Build an integration for GitHub.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 sources/tech/20160817 Build an integration for GitHub.md diff --git a/sources/tech/20160817 Build an integration for GitHub.md b/sources/tech/20160817 Build an integration for GitHub.md new file mode 100644 index 0000000000..2a9c57b3bb --- /dev/null +++ b/sources/tech/20160817 Build an integration for GitHub.md @@ -0,0 +1,54 @@ +Build an integration for GitHub +============= + +![](https://cloud.githubusercontent.com/assets/4432363/17537572/934e2f9c-5e52-11e6-9c24-50c7be5b5ae3.png) + +You can now access more tools from our [Integrations Directory][1]. The Directory now has more than 15 categories — from [API management][2] to [application monitoring][3], there's a GitHub integration to support every stage of your development cycle. + +We're inviting developers of all levels of expertise to create an integration that will help developers work better. If you've built a great integration for GitHub, we want to highlight it! [Gitter][4], [AppVeyor][5], and [ZenHub][6] did it, and so can you. + +### What we're looking for + +Good software development relies on quality tools and developers today have a wide range of choices—language, framework, workflow, and environment among other factors. We're looking for tools that create a better overall development experience. + +#### A couple of guidelines for Integrations Directory listings: + +- Demonstrates solid growth (you should currently have over 500 users via GitHub OAuth) +- Meets our [Technical Requirements][7] +- Follows our [Terms of Service][8] and [Privacy Policy][9] +- Focuses on the software development life cycle + +### Helpful resources + +To get listed, please follow the steps outlined in the [listing requirements page][10]. + +You also should read our[ marketing guidelines][11] and the [existing Directory listings][12] to get a better idea of how to put it all together. Please draft the content for your listing in a [secret gist][13] (markdown format) and email us at . If you have any questions, please don't hesitate to reach out to us at . + +-------------------------------------------------------------------------------- + +via: https://github.com/blog/2226-build-an-integration-for-github + +作者:[chobberoni ][a] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://github.com/chobberoni + +[1]: https://github.com/integrations +[2]: https://github.com/integrations/feature/api-management +[3]: https://github.com/integrations/feature/monitoring +[4]: https://github.com/integrations/feature/monitoring +[5]: https://github.com/integrations/appveyor +[6]: https://github.com/integrations/zenhub +[7]: https://developer.github.com/integrations-directory/getting-listed/#technical-requirements +[8]: https://help.github.com/articles/github-terms-of-service/ +[9]: https://help.github.com/articles/github-privacy-policy/ +[10]: https://developer.github.com/integrations-directory/getting-listed/ +[11]: https://developer.github.com/integrations-directory/marketing-guidelines/ +[12]: https://github.com/integrations +[13]: https://gist.github.com/ + + + From f5ed2e2f57b3c6fa1b2ebb90facf11c1e0be5fff Mon Sep 17 00:00:00 2001 From: GitFuture Date: Wed, 7 Sep 2016 19:07:53 +0800 Subject: [PATCH 4/8] Finish Translation --- ...n for the Android platform 101 - Part 1.md | 109 ---------- ...n for the Android platform 101 - Part 1.md | 188 ++++++++++++++++++ 2 files changed, 188 insertions(+), 109 deletions(-) delete mode 100644 sources/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md create mode 100644 translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md diff --git a/sources/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md b/sources/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md deleted file mode 100644 index 47c186ed0d..0000000000 --- a/sources/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md +++ /dev/null @@ -1,109 +0,0 @@ -Translating by GitFuture - -Dependency Injection for the Android platform 101 - Part 1 -=========================== - -![](https://d262ilb51hltx0.cloudfront.net/max/2000/1*YWlAzAY20KLLGIyyD_mzZw.png) - -When we first start studying software engineering, we usually bump into something like: - ->Software shall be SOLID. - -but what does that mean, in fact? Well, let’s just say that each character of the acronym means something really important for the architecture, such as: - -- [Single Responsibility Principle][1] -- [Open/closed principle][2] -- [Liskov substitution principle][3] -- [Interface segregation principle][4] -- [Dependency inversion principle][5] which is the core concept on which the dependency injection is based. - -Simply, we need to provide a class with all the objects it needs in order to perform its duties. - -### Overview - -Dependency Injection sounds like a very complex term for something that is, in fact, really easy and could be explained with this example: - -As we can see, in the first case, we create the dependency in the constructor, while in the second case it gets passed as a parameter. The second case is what we call dependency injection. We do this so that our class does not depend on the specific implementation of its dependency but it just uses it. -Moreover, based on whether the parameter is passed over to the constructor or to a method, we talk either about constructor dependency injection or method dependency injection: - -If you want to know more about Dependency Injection in general, be sure to check out this amazing talk from Dan Lew that actually inspired this overview. - -On Android, we have different choices when it comes to frameworks for solving this particular problem but the most famous is Dagger 2, first made by the awesome guys at Square and then evolved by Google itself. Specifically, Dagger 1 was made by the first and then Big G took over the project and created the second version, with major changes such being based on annotations and doing its job at compile time. - -### Importing the framework - -Setting up Dagger is no big deal, but it requires us to import the android-apt plugin by adding its dependency in the build.gradle file in the root directory of the project: - -``` -buildscript{ - ... - dependencies{ - ... - classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’ - } -} -``` - -Then, we need to apply the android-apt plugin in the top part of the app’s build.gradle file, right below the Android application one: - -``` -apply plugin: ‘com.neenbedankt.android-apt’ -``` - -At this point, we just need to add the dependencies so that we are now able to use the libraries and its annotations: - -``` -dependencies{ - ... - compile ‘com.google.dagger:dagger:2.6’ - apt ‘com.google.dagger:dagger-compiler:2.6’ - provided ‘javax.annotation:jsr250-api:1.0’ -} -``` - ->The last dependency is needed because the @Generated annotation is not yet available on Android, but it’s pure Java. - -### Dagger Modules - -For injecting our dependencies, we first need to tell the framework what we can provide (i.e. the Context) and how that specific object is built. In order to do this, we annotate a specific class with the @Module annotation (so that Dagger is able to pick it up) scan for the @Provide annotated methods and generate the graph that will give us the object we request. - -Let’s see an example where we create a module that will give us the ConnectivityManager. So we need the Context that we will pass in the constructor of the module: - ->A very interesting feature of Dagger is providing a Singleton by simply annotating a method, dealing with all the issues inherited from Java by itself. - -### The Components - -Once we have a module, we need to tell Dagger where we want our dependencies to be injected: we do this in a Component, a specifically annotated interface in which we create different methods, where the parameters are the classes into which we want our dependencies to be injected. - -Let’s give an example and say that we want our MainActivity class to be able to receive the ConnectivityManager (or any other dependency in the graph). We would simply do something like this: - ->As we can see, the @Component annotation takes some parameters, one being an array of the modules supported, meaning the dependencies it can provide. In our case, that would be both the Context and the ConnectivityManager, as they are declared in the ApplicationModule class. - -### Wiring up - -At this point, what we need to do is create the Component as soon as possible (such as in the onCreate phase of the Application) and return it, so that the classes can use it for injecting the dependencies: - ->In order to have the DaggerApplicationComponent automatically generated by the framework, we need to build our projects so that Dagger can scan our codebase and generate the parts we need. - -In our MainActivity, though, the two things we need to do are annotate a property we want to inject with the @Inject annotation and invoke the method we declared in the ApplicationComponent interface (note that this last part varies based on what kind of injection we are performing, but for the moment we can leave it as is for the simplicity), so that our dependencies get injected and we can use them freely: - -### Conclusion - -Of course, we could do Dependency Injection manually, managing all the different objects, but Dagger takes away a lot of the “noise” involved with such boilerplate, giving us some nice additions (such as Singleton) that would otherwise be pretty awful to deal with in Java. - --------------------------------------------------------------------------------- - -via: https://medium.com/di-101/di-101-part-1-81896c2858a0#.3hg0jj14o - -作者:[Roberto Orgiu][a] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: https://medium.com/@_tiwiz -[1]: https://en.wikipedia.org/wiki/Single_responsibility_principle -[2]: https://en.wikipedia.org/wiki/Open/closed_principle -[3]: http://liskov_substitution_principle/ -[4]: https://en.wikipedia.org/wiki/Interface_segregation_principle -[5]: https://en.wikipedia.org/wiki/Dependency_inversion_principle diff --git a/translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md b/translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md new file mode 100644 index 0000000000..6ae6ddccfb --- /dev/null +++ b/translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md @@ -0,0 +1,188 @@ +安卓平台上的依赖注入 - 第一部分 +=========================== + +![](https://d262ilb51hltx0.cloudfront.net/max/2000/1*YWlAzAY20KLLGIyyD_mzZw.png) + +刚开始学习软件工程的时候,我们经常会碰到像这样的事情: + +>软件应该符合 SOLID 原则。 + +但这句话实际是什么意思?让我们看看 SOLID 中每个字母在架构里所代表的重要含义,例如: + +- [S 单职责原则][1] +- [O 开闭原则][2] +- [L Liskov 替换原则][3] +- [I 接口分离原则][4] +- [D 依赖反转原则][5] 这也是依赖注入的核心概念。 + +简单来说,我们需要提供一个类,这个类有它所需要的所有对象,以便实现其功能。 + +### 概述 + +依赖注入听起来像是描述非常复杂的东西的一个术语,但实际上它很简单,看下面这个例子你就明白了: + +``` +class NoDependencyInjection { + private Dependency d; + + public NoDependencyInjection() { + d = new Dependency(); + } +} + +class DependencyInjection { + private Dependency d; + + public DependencyInjection(Dependency d) { + this.d = d; + } +} +``` + +正如我们所见,第一种情况是我们在构造器里创建了依赖对象,但在第二种情况下,它作为参数被传递给构造器,这就是我们所说的依赖注入。这样做是为了让我们所写的类不依靠特定依赖关系的实现,却能直接使用它。 + +参数传递的目标是构造器,我们就称之为构造器依赖注入;或者是某个方法,就称之为方法依赖注入: + +``` +class Example { + private ConstructorDependency cd; + private MethodDependency md; + Example(ConstructorDependency cd) { + this.cd = cd; //Constructor Dependency Injection + } + + public setMethodDependency(MethodDependency md) { + this.md = md; //Method Dependency Injection + } +} +``` + + +要是你想总体深入地了解依赖注入,可以看看由 [Dan Lew][t2] 发表的[精彩的演讲][t1],事实上是这个演讲启迪了这个概述。 + +在 Android 平台,当需要框架来处理依赖注入这个特殊的问题时,我们有不同的选择,其中最有名的框架就是 [Dagger 2][t3]。它最开始是由 Square 公司(译者注:Square 是美国一家移动支付公司)里一些很棒的开发者开发出来的,然后慢慢发展成由 Google 自己开发。特别地,Dagger 1 先被开发出来,然后 Big G 接手这个项目,做了很多改动,比如以注释为基础,在编译的时候就完成 Dagger 的任务,也就是第二个版本。 + +### 导入框架 + +安装 Dagger 并不难,但需要导入 `android-apt` 插件,通过向项目的根目录下的 build.gradle 文件中添加它的依赖关系: + +``` +buildscript{ + ... + dependencies{ + ... + classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’ + } +} +``` + +然后,我们需要将 `android-apt` 插件应用到项目 build.gradle 文件,放在文件顶部 Android 应用那一句的下一行: + +``` +apply plugin: ‘com.neenbedankt.android-apt’ +``` + +这个时候,我们只用添加依赖关系,然后就能使用库和注释了: + +``` +dependencies{ + ... + compile ‘com.google.dagger:dagger:2.6’ + apt ‘com.google.dagger:dagger-compiler:2.6’ + provided ‘javax.annotation:jsr250-api:1.0’ +} +``` + +>需要加上最后一个依赖关系是因为 @Generated 注解在 Android 里还不可用,但它是[原生的 Java 注解][t4]。 + +### Dagger 模块 + +要注入依赖,首先需要告诉框架我们能提供什么(比如说上下文)以及特定的对象应该怎样创建。为了完成注入,我们用 `@Module` 注释对一个特殊的类进行了注解(这样 Dagger 就能识别它了),寻找 `@Provide` 标记的方法,生成图表,能够返回我们所请求的对象。 + +看下面的例子,这里我们创建了一个模块,它会返回给我们 `ConnectivityManager`,所以我们要把 `Context` 对象传给这个模块的构造器。 + +``` +@Module +public class ApplicationModule { + private final Context context; + + public ApplicationModule(Context context) { + this.context = context; + } + + @Provides @Singleton + public Context providesContext() { + return context; + } + + @Provides @Singleton + public ConnectivityManager providesConnectivityManager(Context context) { + return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + } +} +``` + +>Dagger 中十分有意思的一点是只用在一个方法前面添加一个 Singleton 注解,就能处理所有从 Java 中继承过来的问题。 + +### 容器 + +当我们有一个模块的时候,我们需要告诉 Dagger 想把依赖注入到哪里:我们在一个容器里,一个特殊的注解过的接口里完成依赖注入。我们在这个接口里创造不同的方法,而接口的参数是我们想注入依赖关系的类。 + +下面给出一个例子并告诉 Dagger 我们想要 `MainActivity` 类能够接受 `ConnectivityManager`(或者在图表里的其它依赖对象)。我们只要做类似以下的事: + +``` +@Singleton +@Component(modules = {ApplicationModule.class}) +public interface ApplicationComponent { + + void inject(MainActivity activity); +} +``` + +>正如我们所见,@Component 注解有几个参数,一个是所支持的模块的数组,意味着它能提供的依赖。这里既可以是 Context 也可以是 ConnectivityManager,因为他们在 ApplicationModule 类中有声明。 + +### 使用 + +这时,我们要做的是尽快创建容器(比如在应用的 onCreate 方法里面)并且返回这个容器,那么类就能用它来注入依赖了: + +>为了让框架自动生成 DaggerApplicationComponent,我们需要构建项目以便 Dagger 能够扫描我们的代码库,并且生成我们需要的部分。 + +在 `MainActivity` 里,我们要做的两件事是用 `@Inject` 注解符对想要注入的属性进行注释,调用我们在 `ApplicationComponent` 接口中声明的方法(请注意后面一部分会因我们使用的注入类型的不同而变化,但这里简单起见我们不去管它),然后依赖就被注入了,我们就能自由使用他们: + +``` +public class MainActivity extends AppCompatActivity { + @Inject + ConnectivityManager manager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + ... + ((App) getApplication()).getComponent().inject(this); + } +} +``` + +### 总结 + +当然了,我们可以手动注入依赖,管理所有不同的对象,但 Dagger 打消了很多有关模板的“噪声”,Dagger 给我们有用的附加品(比如 `Singleton`),而仅用 Java 处理将会很糟糕。 + +-------------------------------------------------------------------------------- + +via: https://medium.com/di-101/di-101-part-1-81896c2858a0#.3hg0jj14o + +作者:[Roberto Orgiu][a] +译者:[GitFuture](https://github.com/GitFuture) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://medium.com/@_tiwiz +[1]: https://en.wikipedia.org/wiki/Single_responsibility_principle +[2]: https://en.wikipedia.org/wiki/Open/closed_principle +[3]: http://liskov_substitution_principle/ +[4]: https://en.wikipedia.org/wiki/Interface_segregation_principle +[5]: https://en.wikipedia.org/wiki/Dependency_inversion_principle +[t1]: https://realm.io/news/daniel-lew-dependency-injection-dagger/ +[t2]: https://twitter.com/danlew42 +[t3]: http://google.github.io/dagger/ +[t4]: https://docs.oracle.com/javase/7/docs/api/javax/annotation/Generated.html From f1fe0d05c0c7811378e656717db35f3dfc93ae2e Mon Sep 17 00:00:00 2001 From: John Joe Date: Wed, 7 Sep 2016 20:50:26 +0800 Subject: [PATCH 5/8] Update 20160820 Building your first Atom plugin.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 翻译选题 --- sources/tech/20160820 Building your first Atom plugin.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sources/tech/20160820 Building your first Atom plugin.md b/sources/tech/20160820 Building your first Atom plugin.md index 76915cc37d..8cecc9c09f 100644 --- a/sources/tech/20160820 Building your first Atom plugin.md +++ b/sources/tech/20160820 Building your first Atom plugin.md @@ -1,3 +1,4 @@ +Tranlating by lengmi. Building your first Atom plugin ===== From 26b001a81403255fc38f119fe735bd230f60a304 Mon Sep 17 00:00:00 2001 From: vim-kakali <1799225723@qq.com> Date: Wed, 7 Sep 2016 20:58:15 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md b/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md index 44c66359f9..e0f69aca84 100644 --- a/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md +++ b/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md @@ -1,3 +1,6 @@ +vim-kakali translating + + TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016 ===================================================== From 293f8705fa5cb34de1b852a57334077afb7312e2 Mon Sep 17 00:00:00 2001 From: wxy Date: Thu, 8 Sep 2016 06:55:10 +0800 Subject: [PATCH 7/8] PUB:20160509 Android vs. iPhone Pros and Cons @joVoV --- ...160509 Android vs. iPhone Pros and Cons.md | 89 +++++++++++++++++++ ...160509 Android vs. iPhone Pros and Cons.md | 87 ------------------ 2 files changed, 89 insertions(+), 87 deletions(-) create mode 100644 published/20160509 Android vs. iPhone Pros and Cons.md delete mode 100644 translated/talk/20160509 Android vs. iPhone Pros and Cons.md diff --git a/published/20160509 Android vs. iPhone Pros and Cons.md b/published/20160509 Android vs. iPhone Pros and Cons.md new file mode 100644 index 0000000000..76993e842c --- /dev/null +++ b/published/20160509 Android vs. iPhone Pros and Cons.md @@ -0,0 +1,89 @@ +对比 Android 和 iPhone 的优缺点 +=================================== + +>当我们比较 Android 与 iPhone 的时候,很显然 Android 具有一定的优势,而 iPhone 则在一些关键方面更好。但是,究竟哪个比较好呢? + +对 Android 与 iPhone 比较是个个人的问题。 + +就好比我来说,我两个都用。我深知这两个平台的优缺点。所以,我决定分享我关于这两个移动平台的观点。另外,然后谈谈我对新的 Ubuntu 移动平台的印象和它的优势。 + +### iPhone 的优点 + +虽然这些天我是个十足的 Android 用户,但我必须承认 iPhone 在某些方面做的是不错。首先,苹果公司在他们的设备更新方面有更好的成绩。这对于运行着 iOS 的旧设备来说尤其是这样。反观 Android ,如果不是谷歌亲生的 Nexus,它最好也不过是一个更高端的运营商支持的手机,你将发现它们的更新少的可怜或者根本没有。 + +其中 iPhone 做得很好的另一个领域是应用程序的可用性。展开来说,iPhone 应用程序几乎总是有一个简洁的外观。这并不是说 Android 应用程序就是丑陋的,而是,它们可能只是没有像 iOS 上一样的保持不变的操控习惯和一以贯之的用户体验。两个典型的例子, [Dark Sky][1] (天气)和 [Facebook Paper][2] 很好表现了 iOS 独有的布局。 + +再有就是备份过程。 Android 可以备份,默认情况下是备份到谷歌。但是对应用数据起不了太大作用。对比 iPhone ,iCloud 基本上可以对你的 iOS 设备进行了完整备份。 + +### iPhone 令我失望的地方 + +对 iPhone 来说,最无可争辩的问题是它的硬件限制要比软件限制更大,换句话来说,就是存储容量问题。 + +你看,对于大多数 Android 手机,我可以买一个容量较小的手机,然后以后可以添加 SD 卡。这意味着两件事:第一,我可以使用 SD 卡来存储大量的媒体文件。其次,我甚至可以用 SD 卡来存储“一些”我的应用程序。而苹果完全不能这么做。 + +另一个 iPhone 让我失望的地方是它提供的选择很少。备份您的设备?希望你喜欢 iTunes 或 iCloud 吧。但对一些像我一样用 Linux 的人,那就意味着,我唯一的选择便是使用 iCloud。 + +要公平的说,如果你愿意越狱,你的 iPhone 还有一些其他解决方案的,但这并不是这篇文章所讲的。 Android 的 解锁 root 也一样。本文章针对的是两个平台的原生设置。 + +最后,让我们不要忘记这件看起来很小的事—— [iTunes 会删掉用户的音乐][3],因为它认为和苹果音乐的内容重复了……或者因为一些其它的类似规定。这不是 iPhone 特有的情况?我不同意,因为那些音乐最终就是在 iPhone 上没有了。我能十分肯定地这么说,是因为不管在哪里我都不会说这种谎话。 + +![](http://www.datamation.com/imagesvr_ce/5552/mobile-abstract-icon-200x150.jpg) + +*Android 和 iPhone 的对决取决于什么功能对你来说最重要。* + +### Android 的优点 + +Android 给我最大的好处就是 iPhone 所提供不了的:选择。这包括对应用程序、设备以及手机是整体如何工作的选择。 + +我爱桌面小工具!对于 iPhone 用户来说,它们也许看上去很蠢。但我可以告诉你,它们可以让我不用打开应用程序就可以看到所需的数据,而无需额外的麻烦。另一个类似的功能,我喜欢安装定制的桌面界面,而不是我的手机默认的那个! + +最后,我可以通过像 [Airdroid][4] 和 [Tasker][5] 这样的工具给我的智能手机添加计算机级的完整功能。AirDroid 可以让我把我的 Android 手机当成带有一个文件管理和通信功能的计算机——这可以让我可以轻而易举的使用鼠标和键盘。Tasker 更厉害,我可以用它让我手机根据环境变得可联系或不可联系,当我设置好了之后,当我到会议室之后我的手机就会自己进入会议模式,甚至变成省电模式。我还可以设置它当我到达特定的目的地时自动启动某个应用程序。 + +### Android 让我失望的地方 + +Android 备份选项仅限于特定的用户数据,而不是手机的完整克隆。如果不解锁 root,要么你只能听之任之,要么你必须用 Android SDK 来解决。期望普通用户会解锁 root 或运行 SDK 来完成备份(我的意思是一切都备份)显然是一个笑话。 + +是的,谷歌的备份服务会备份谷歌应用程序的数据、以及其他相关的自定义设置。但它是远不及我们所看到的苹果服务一样完整。为了完成类似于苹果那样的功能,我发现你就要么必须解锁 root ,要么将其连接到一个在 PC 机上使用一些不知道是什么的软件来干这个。 + +不过,公平的说,我知道使用 Nexus 的人能从该设备特有的[完整备份服务][6]中得到帮助。对不起,但是谷歌的默认备份方案是不行的。对于通过在 PC 上使用 adb (Android Debug Bridge) 来备份也是一样的——不会总是如预期般的恢复。 + +等吧,它会变好的。经过了很多失败的失望和挫折之后,我发现有一个应用程序,看起来它“可能”提供了一点点微小的希望,它叫 Helium 。它不像我发现的其他应用程序那样拥有误导性的和令人沮丧的局限性,[Helium][7] 最初看起来像是谷歌应该一直提供的备份应用程序——注意,只是“看起来像”。可悲的是,它绊了我一跤。第一次运行时,我不仅需要将它连接到我的计算机上,甚至使用他们提供的 Linux 脚本都不工作。在删除他们的脚本后,我弄了一个很好的老式 adb 来备份到我的 Linux PC 上。你可能要知道的是:你需要在开发工具里打开一箩筐东西,而且如果你运行 Twilight 应用的话还要关闭它。当 adb 的备份选项在我的手机上不起作用时,我花了一点时间把这个搞好了。 + +最后,Android 为非 root 用户也提供了可以轻松备份一些如联系人、短信等简单东西的选择。但是,要深度手机备份的话,以我经验还是通过有线连接和 adb。 + +### Ubuntu 能拯救我们吗? + +在手机领域,通过对这两大玩家之间的优劣比较,我们很期望从 Ubuntu 看到更好的表现。但是,迄今为止,它的表现相当低迷。 + +我喜欢开发人员基于这个操作系统正在做的那些努力,我当然想在 iPhone 和 Android 手机之外有第三种选择。但是不幸的是,它在手机和平板上并不受欢迎,而且由于硬件的低端以及在 YouTube 上的糟糕的演示,有很多不好的传闻。 + +公平来说,我在以前也用过 iPhone 和 Android 的低端机,所以这不是对 Ubuntu 的挖苦。但是它要表现出准备与 iPhone 和 Android 相竞争的功能生态时,那就另说了,这还不是我现在特别感兴趣的东西。在以后的日子里,也许吧,我会觉得 Ubuntu 手机可以满足我的需要了。 + +###Android 与 iPhone 之争:为什么 Android 将终究赢得胜利 + +忽视 Android 那些痛苦的缺点,它起码给我了选择。它并没有把我限制在只有两种备份数据的方式上。是的,一些 Android 的限制事实上是由于它的关注点在于让我选择如何处理我的数据。但是,我可以选择我自己的设备,想加内存就加内存。Android 可以我让做很多很酷的东西,而 iPhone 根本就没有能力这些事情。 + +从根本上来说, Android 给非 root 用户提供了访问手机功能的更大自由。无论是好是坏,这是人们想要的一种自由。现在你们其中有很多 iPhone 的粉丝应该感谢像 [libimobiledevice][8] 这样项目带来的影响。看看苹果阻止 Linux 用户所做的事情……然后问问你自己:作为一个 Linux 用户这真的值得吗? + +发表下评论吧,分享你对 iPhone 、Android 或 Ubuntu 的看法。 + +------------------------------------------------------------------------------ + +via: http://www.datamation.com/mobile-wireless/android-vs.-iphone-pros-and-cons.html + +作者:[Matt Hartley][a] +译者:[jovov](https://github.com/jovov) +校对:[wxy](https://github.com/wxy) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: http://www.datamation.com/author/Matt-Hartley-3080.html +[1]: http://darkskyapp.com/ +[2]: https://www.facebook.com/paper/ +[3]: https://blog.vellumatlanta.com/2016/05/04/apple-stole-my-music-no-seriously/ +[4]: https://www.airdroid.com/ +[5]: http://tasker.dinglisch.net/ +[6]: https://support.google.com/nexus/answer/2819582?hl=en +[7]: https://play.google.com/store/apps/details?id=com.koushikdutta.backup&hl=en +[8]: http://www.libimobiledevice.org/ + diff --git a/translated/talk/20160509 Android vs. iPhone Pros and Cons.md b/translated/talk/20160509 Android vs. iPhone Pros and Cons.md deleted file mode 100644 index efbef243b9..0000000000 --- a/translated/talk/20160509 Android vs. iPhone Pros and Cons.md +++ /dev/null @@ -1,87 +0,0 @@ -Android 和 iPhone 的优缺点 -=================================== - - ->当我们比较 Android 与 iPhone 的时候,很显然 iPhone 和 Android 在一些关键方面都具有一定的优势,但是,究竟哪个比较好呢? - - Android 与 iPhone 两者比较是个私人的问题。 - -就好比我来说,我两个都用。我深知这两个平台的优劣势。所以,我决定分享关于这两个移动平台的观点。另外,然后谈谈我对新的 Ubuntu 移动平台的印象和它的优势。 - -### iPhone 的优点 - -虽然这些天我是个十足的 Android 用户,但我必须承认到 iPhone 在某些方面做的是不错。首先,苹果公司在更新他们的设备有更好的战绩。尤其是在旧设备也能运行 iOS 。反观 Android ,如果它不是谷歌福利的关系,它最好是一个更高端的运营商支持的手机。否则,你将发现找那些更新少的可怜或者不存在。 - -其中 iPhone 做得很好的另一个领域是应用程序的可用性。扩展上: iPhone 应用程序几乎总是有一个简洁的外观。这并不是说 Android 应用程序是难看的,相反,他们可能没有和预期的流动性和一致性当建立在 iOS 上。有两个例子 [Dark Sky][1] (天气)和 [Facebook Paper][2] 很好表现了 iOS 的布局。 - -再有就是备份过程。 Android 可以备份,默认情况下是备份到谷歌。但是对应用数据起不了太多作用。对比 iPhone ,iCloud 基本上可以让你的 iOS 设备进行完整备份。 - -### iPhone 令我失望的地方 - -我使用 iPhone 的最大的不容置疑的问题是它的硬件限制大于软件,换句话来说,就是存储问题。 - -你看,对于大多数 Android 手机,我可以买一个容量较小的手机,然后在以后添加 SD 卡。这做了两件事:第一,我可以使用 SD 卡来存储大量的媒体文件。其次,我甚至可以用 SD 卡来存储我的应用程序的一些文件。苹果完全不能这么做。 - -另一个 iPhone 让我失望的地方是它提供的选择很少。备份您的设备?希望大家喜欢 iTunes 或 iCloud 。但对一些像我一样用 Linux 的人,那就意味着,我唯一的选择便是使用 iCloud 。 - -为了最终公平的,如果你愿意越狱,你的 iPhone 还有一些其他解决方案的。但这并不是文章所讲的。 Android 的 root 也一样。本文章针对的是两个用户的原生设置。 - -最后,让我们不要忘记这小小的玩意儿—— [iTunes 决定删除用户的音乐][3] 因为它被视为苹果音乐内容的重复...或者类似的规定。 iPhone 并不明确?我不同意,就算音乐在有些时候很好地在结束在 iPhone 上。我也十分肯定地说在任何地方我会不会忍受这种废话! - -![](http://www.datamation.com/imagesvr_ce/5552/mobile-abstract-icon-200x150.jpg) -> Android 和 iPhone 的对决取决于什么功能对你来说最重要。 - -### Android 的优点 - - Android 给我最大的事情就是 iPhone 提供不了的选择。包括应用程序,设备和我的手机是如何工作的整体布局。 - -我爱桌面小工具!对于 iPhone 用户,它们也许看上去很蠢。但我可以告诉你,他们可以让我不用打开应用程序就可以看到所需的数据,而无需额外的麻烦。另一个类似的功能,我喜欢安装自定义应用,而不是我的手机的默认! - -最后,我可以利用像 [Airdroid][4] 和 [Tasker][5] 工具添加全电脑式的功能到我的智能手机。AirDroid 可以让我对待我的 Android 手机就像一个文件管理和通信的计算机–这使得使用我的鼠标和键盘变得轻而易举的。Tasker 很厉害,我可以用它让我手机可联系或不可联系,当我设置参数时,我可以把我的手机处在会议模式,甚至把它自己变成省电模式。我甚至可以设置它来启动应用程序时当我到达特定的目的地时。 - -### Android 让我失望的地方 - -备份选项仅限于特定的用户数据,而不是手机的完整克隆。没有 root ,你将要么乘风而去或者你必须看看 Android SDK 开发解决方案。期望普通用户 root 他们的手机或运行 SDK来进行所有的Android的备份(我的意思是一切)将是一个笑话。 - -是的,谷歌的备份服务将备份谷歌应用程序的数据,以及其他相关的自定义设置。但它是远不及我们所看到的苹果一样完整。为了完成类似于在苹果的功能,我发现你就必须要 root 你的安卓手机或利用一些随机程序将其连接到一个在 PC 机上来。 - -为了公平的,但是,我相信 Nexus 所有者受益于一个 [完整备份服务][6] ,这是设备特定。对不起,但谷歌的默认备份是不削减它。同样的应用来备份您的电脑——他们不总是恢复预期的东西。 - -等待,它会变得更好。现在经过了很多失败的失望和挫折,我发现有一个应用程序,看起来它“可能”提供了一个微小的希望,它被称为 Helium 。它不像我发现的其他应用程序那样拥有误导性的和令人沮丧的局限性,[Helium][7] 最初看起来像是谷歌应该一直提供的备份应用程序——强调“看起来像”。可悲的是,这是一个巨大的失望。我不仅需要将它连接到我的计算机上进行第一次运行,而且它甚至不使用他们提供的 Linux 脚本。删除他们的脚本后,我弄了一个很好的老式 adb (Android Debug Bridge) 备份到我的Linux PC 。有趣的事实:你需要在开发工具里打开一箩筐东西,再加上如果你运行 Twilight app,那是需要被关闭的。当 adb (Android Debug Bridge) 的备份选项在手机上不起作用时,它花了我一点时间把这个弄在一起。 - -最终,Android 为非 root 用户也提供了可以轻松备份一些如联系人,短信等简单东西的选择。但是,要深度手机备份的话,以我经验还是通过有线连接和 adb (Android Debug Bridge) 。 - -### Ubuntu 会救我们吗? - -在手机领域,通过两大玩家之间的好坏考核,我们将有很多的希望从 Ubuntu 看到好的一方面。但是,迄今为止,它已经相当低迷。 - -我喜欢开发人员正在基于 OS 所做的,我当然喜欢除了 iPhone 和 Android 手机的第三个选项的想法。但是不幸的是,它在手机和平板上并不受欢迎且受到很多坏新闻,就是由于不符合标准的硬件和一个 YouTube 上的糟糕的示范。 - -公平来说,我在以前用 iPhone 和 Android 也不是很规范。所以这不是对 Ubuntu 的挖苦。但是直到它开始表现出准备提供生态系统功能来与 iPhone 和 Android 竞争,那就另说了,这还不是我现在特别感兴趣的东西。在以后的日子里,也许,我会觉得 Ubuntu 手机可以满足我的需要了。 - -###Android pk iPhone:为什么Android 长期胜利 - -忽视 Android 那些痛苦的缺点,它起码对待我像一个成年人。它并没有把我困在只有两种方法来备份我的数据。是的,一些 Android 的限制是由于它的关注点在让我选择如何处理我的数据。但是,我也可以选择我自己的设备,一时兴起扩充内存。 Android 让我做了很多很酷的东西,那些手机根本就没有能力做的事情。 - -在其核心, Android 给非 root 用户提供更大的访问手机的功能。无论是好是坏,这是人们倾向的一种自由。现在你们其中有很多用 iPhone 谩骂的人多亏了像 [libimobiledevice][8] 类似影响的项目。但要看看苹果阻止 Linux 用户所做的事情……然后问自己:作为一个 Linux 用户这是真的值得吗?评论,分享你对 iPhone 、 Android 或 Ubuntu 的看法。 - ------------------------------------------------------------------------------- - -via: http://www.datamation.com/mobile-wireless/android-vs.-iphone-pros-and-cons.html - -作者:[Matt Hartley][a] -译者:[jovov](https://github.com/jovov) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: http://www.datamation.com/author/Matt-Hartley-3080.html -[1]: http://darkskyapp.com/ -[2]: https://www.facebook.com/paper/ -[3]: https://blog.vellumatlanta.com/2016/05/04/apple-stole-my-music-no-seriously/ -[4]: https://www.airdroid.com/ -[5]: http://tasker.dinglisch.net/ -[6]: https://support.google.com/nexus/answer/2819582?hl=en -[7]: https://play.google.com/store/apps/details?id=com.koushikdutta.backup&hl=en -[8]: http://www.libimobiledevice.org/ - From cfd8d78fc87e1135bfa4360ec69720764a6c4163 Mon Sep 17 00:00:00 2001 From: wxy Date: Thu, 8 Sep 2016 14:18:07 +0800 Subject: [PATCH 8/8] PUB:20160527 A Python Interpreter Written in Python MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @qingyunha 好长的文章,翻译的不错! --- ... A Python Interpreter Written in Python.md | 205 +++++++++--------- 1 file changed, 100 insertions(+), 105 deletions(-) rename {translated/tech => published}/20160527 A Python Interpreter Written in Python.md (53%) diff --git a/translated/tech/20160527 A Python Interpreter Written in Python.md b/published/20160527 A Python Interpreter Written in Python.md similarity index 53% rename from translated/tech/20160527 A Python Interpreter Written in Python.md rename to published/20160527 A Python Interpreter Written in Python.md index dbf8dc7cf4..18c2feedd7 100644 --- a/translated/tech/20160527 A Python Interpreter Written in Python.md +++ b/published/20160527 A Python Interpreter Written in Python.md @@ -1,37 +1,37 @@ -用Python实现Python解释器 +用 Python 实现 Python 解释器 === -_Allison是Dropbox的工程师,在那里她维护着世界上最大的由Python客户组成的网络。在Dropbox之前,她是Recurse Center的导师, 曾在纽约写作。在北美的PyCon做过关于Python内部机制的演讲,并且她喜欢奇怪的bugs。她的博客地址是[akaptur.com](http://akaptur.com)._ +_Allison 是 Dropbox 的工程师,在那里她维护着这个世界上最大的 Python 客户端网络之一。在去 Dropbox 之前,她是 Recurse Center 的协调人, 是这个位于纽约的程序员深造机构的作者。她在北美的 PyCon 做过关于 Python 内部机制的演讲,并且她喜欢研究奇怪的 bug。她的博客地址是 [akaptur.com](http://akaptur.com)。_ -## Introduction +### 介绍 -Byterun是一个用Python实现的Python解释器。随着我在Byterun上的工作,我惊讶并很高兴地的发现,这个Python解释器的基础结构可以满足500行的限制。在这一章我们会搞清楚这个解释器的结构,给你足够的知识探索下去。我们的目标不是向你展示解释器的每个细节---像编程和计算机科学其他有趣的领域一样,你可能会投入几年的时间去搞清楚这个主题。 +Byterun 是一个用 Python 实现的 Python 解释器。随着我对 Byterun 的开发,我惊喜地的发现,这个 Python 解释器的基础结构用 500 行代码就能实现。在这一章我们会搞清楚这个解释器的结构,给你足够探索下去的背景知识。我们的目标不是向你展示解释器的每个细节---像编程和计算机科学其他有趣的领域一样,你可能会投入几年的时间去深入了解这个主题。 -Byterun是Ned Batchelder和我完成的,建立在Paul Swartz的工作之上。它的结构和主要的Python实现(CPython)差不多,所以理解Byterun会帮助你理解大多数解释器特别是CPython解释器。(如果你不知道你用的是什么Python,那么很可能它就是CPython)。尽管Byterun很小,但它能执行大多数简单的Python程序。 +Byterun 是 Ned Batchelder 和我完成的,建立在 Paul Swartz 的工作之上。它的结构和主要的 Python 实现(CPython)差不多,所以理解 Byterun 会帮助你理解大多数解释器,特别是 CPython 解释器。(如果你不知道你用的是什么 Python,那么很可能它就是 CPython)。尽管 Byterun 很小,但它能执行大多数简单的 Python 程序(这一章是基于 Python 3.5 及其之前版本生成的字节码的,在 Python 3.6 中生成的字节码有一些改变)。 -### A Python Interpreter +#### Python 解释器 -在开始之前,让我们缩小一下“Pyhton解释器”的意思。在讨论Python的时候,“解释器”这个词可以用在很多不同的地方。有的时候解释器指的是REPL,当你在命令行下敲下`python`时所得到的交互式环境。有时候人们会相互替代的使用Python解释器和Python来说明执行Python代码的这一过程。在本章,“解释器”有一个更精确的意思:执行Python程序过程中的最后一步。 +在开始之前,让我们限定一下“Pyhton 解释器”的意思。在讨论 Python 的时候,“解释器”这个词可以用在很多不同的地方。有的时候解释器指的是 Python REPL,即当你在命令行下敲下 `python` 时所得到的交互式环境。有时候人们会或多或少的互换使用 “Python 解释器”和“Python”来说明从头到尾执行 Python 代码的这一过程。在本章中,“解释器”有一个更精确的意思:Python 程序的执行过程中的最后一步。 -在解释器接手之前,Python会执行其他3个步骤:词法分析,语法解析和编译。这三步合起来把源代码转换成_code object_,它包含着解释器可以理解的指令。而解释器的工作就是解释code object中的指令。 +在解释器接手之前,Python 会执行其他 3 个步骤:词法分析,语法解析和编译。这三步合起来把源代码转换成代码对象(code object),它包含着解释器可以理解的指令。而解释器的工作就是解释代码对象中的指令。 -你可能很奇怪执行Python代码会有编译这一步。Python通常被称为解释型语言,就像Ruby,Perl一样,它们和编译型语言相对,比如C,Rust。然而,这里的术语并不是它看起来的那样精确。大多数解释型语言包括Python,确实会有编译这一步。而Python被称为解释型的原因是相对于编译型语言,它在编译这一步的工作相对较少(解释器做相对多的工作)。在这章后面你会看到,Python的编译器比C语言编译器需要更少的关于程序行为的信息。 +你可能很奇怪执行 Python 代码会有编译这一步。Python 通常被称为解释型语言,就像 Ruby,Perl 一样,它们和像 C,Rust 这样的编译型语言相对。然而,这个术语并不是它看起来的那样精确。大多数解释型语言包括 Python 在内,确实会有编译这一步。而 Python 被称为解释型的原因是相对于编译型语言,它在编译这一步的工作相对较少(解释器做相对多的工作)。在这章后面你会看到,Python 的编译器比 C 语言编译器需要更少的关于程序行为的信息。 -### A Python Python Interpreter +#### Python 的 Python 解释器 -Byterun是一个用Python写的Python解释器,这点可能让你感到奇怪,但没有比用C语言写C语言编译器更奇怪。(事实上,广泛使用的gcc编译器就是用C语言本身写的)你可以用几乎的任何语言写一个Python解释器。 +Byterun 是一个用 Python 写的 Python 解释器,这点可能让你感到奇怪,但没有比用 C 语言写 C 语言编译器更奇怪的了。(事实上,广泛使用的 gcc 编译器就是用 C 语言本身写的)你可以用几乎任何语言写一个 Python 解释器。 -用Python写Python既有优点又有缺点。最大的缺点就是速度:用Byterun执行代码要比用CPython执行慢的多,CPython解释器是用C语言实现的并做了优化。然而Byterun是为了学习而设计的,所以速度对我们不重要。使用Python最大优点是我们可以*仅仅*实现解释器,而不用担心Python运行时的部分,特别是对象系统。比如当Byterun需要创建一个类时,它就会回退到“真正”的Python。另外一个优势是Byterun很容易理解,部分原因是它是用高级语言写的(Python!)(另外我们不会对解释器做优化 --- 再一次,清晰和简单比速度更重要) +用 Python 写 Python 既有优点又有缺点。最大的缺点就是速度:用 Byterun 执行代码要比用 CPython 执行慢的多,CPython 解释器是用 C 语言实现的,并做了认真优化。然而 Byterun 是为了学习而设计的,所以速度对我们不重要。使用 Python 最大优势是我们可以*仅仅*实现解释器,而不用担心 Python 运行时部分,特别是对象系统。比如当 Byterun 需要创建一个类时,它就会回退到“真正”的 Python。另外一个优势是 Byterun 很容易理解,部分原因是它是用人们很容易理解的高级语言写的(Python !)(另外我们不会对解释器做优化 --- 再一次,清晰和简单比速度更重要) -## Building an Interpreter +### 构建一个解释器 -在我们考察Byterun代码之前,我们需要一些对解释器结构的高层次视角。Python解释器是如何工作的? +在我们考察 Byterun 代码之前,我们需要从高层次对解释器结构有一些了解。Python 解释器是如何工作的? -Python解释器是一个_虚拟机_,模拟真实计算机的软件。我们这个虚拟机是栈机器,它用几个栈来完成操作(与之相对的是寄存器机器,它从特定的内存地址读写数据)。 +Python 解释器是一个虚拟机(virtual machine),是一个模拟真实计算机的软件。我们这个虚拟机是栈机器(stack machine),它用几个栈来完成操作(与之相对的是寄存器机器(register machine),它从特定的内存地址读写数据)。 -Python解释器是一个_字节码解释器_:它的输入是一些命令集合称作_字节码_。当你写Python代码时,词法分析器,语法解析器和编译器生成code object让解释器去操作。每个code object都包含一个要被执行的指令集合 --- 它就是字节码 --- 另外还有一些解释器需要的信息。字节码是Python代码的一个_中间层表示_:它以一种解释器可以理解的方式来表示源代码。这和汇编语言作为C语言和机器语言的中间表示很类似。 +Python 解释器是一个字节码解释器(bytecode interpreter):它的输入是一些称作字节码(bytecode)的指令集。当你写 Python 代码时,词法分析器、语法解析器和编译器会生成代码对象(code object)让解释器去操作。每个代码对象都包含一个要被执行的指令集 —— 它就是字节码 —— 以及还有一些解释器需要的信息。字节码是 Python 代码的一个中间层表示( intermediate representation):它以一种解释器可以理解的方式来表示源代码。这和汇编语言作为 C 语言和机器语言的中间表示很类似。 -### A Tiny Interpreter +#### 微型解释器 为了让说明更具体,让我们从一个非常小的解释器开始。它只能计算两个数的和,只能理解三个指令。它执行的所有代码只是这三个指令的不同组合。下面就是这三个指令: @@ -39,7 +39,7 @@ Python解释器是一个_字节码解释器_:它的输入是一些命令集合 - `ADD_TWO_VALUES` - `PRINT_ANSWER` -我们不关心词法,语法和编译,所以我们也不在乎这些指令是如何产生的。你可以想象,你写下`7 + 5`,然后一个编译器为你生成那三个指令的组合。如果你有一个合适的编译器,你甚至可以用Lisp的语法来写,只要它能生成相同的指令。 +我们不关心词法、语法和编译,所以我们也不在乎这些指令集是如何产生的。你可以想象,当你写下 `7 + 5`,然后一个编译器为你生成那三个指令的组合。如果你有一个合适的编译器,你甚至可以用 Lisp 的语法来写,只要它能生成相同的指令。 假设 @@ -58,13 +58,13 @@ what_to_execute = { "numbers": [7, 5] } ``` -Python解释器是一个_栈机器_,所以它必须通过操作栈来完成这个加法。(\aosafigref{500l.interpreter.stackmachine}.)解释器先执行第一条指令,`LOAD_VALUE`,把第一个数压到栈中。接着它把第二个数也压到栈中。然后,第三条指令,`ADD_TWO_VALUES`,先把两个数从栈中弹出,加起来,再把结果压入栈中。最后一步,把结果弹出并输出。 +Python 解释器是一个栈机器(stack machine),所以它必须通过操作栈来完成这个加法(见下图)。解释器先执行第一条指令,`LOAD_VALUE`,把第一个数压到栈中。接着它把第二个数也压到栈中。然后,第三条指令,`ADD_TWO_VALUES`,先把两个数从栈中弹出,加起来,再把结果压入栈中。最后一步,把结果弹出并输出。 -\aosafigure[240pt]{interpreter-images/interpreter-stack.png}{A stack machine}{500l.interpreter.stackmachine} +![栈机器](http://aosabook.org/en/500L/interpreter-images/interpreter-stack.png) -`LOAD_VALUE`这条指令告诉解释器把一个数压入栈中,但指令本身并没有指明这个数是多少。指令需要一个额外的信息告诉解释器去哪里找到这个数。所以我们的指令集有两个部分:指令本身和一个常量列表。(在Python中,字节码就是我们称为的“指令”,而解释器执行的是_code object_。) +`LOAD_VALUE`这条指令告诉解释器把一个数压入栈中,但指令本身并没有指明这个数是多少。指令需要一个额外的信息告诉解释器去哪里找到这个数。所以我们的指令集有两个部分:指令本身和一个常量列表。(在 Python 中,字节码就是我们所称的“指令”,而解释器“执行”的是代码对象。) -为什么不把数字直接嵌入指令之中?想象一下,如果我们加的不是数字,而是字符串。我们可不想把字符串这样的东西加到指令中,因为它可以有任意的长度。另外,我们这种设计也意味着我们只需要对象的一份拷贝,比如这个加法 `7 + 7`, 现在常量表 `"numbers"`只需包含一个`7`。 +为什么不把数字直接嵌入指令之中?想象一下,如果我们加的不是数字,而是字符串。我们可不想把字符串这样的东西加到指令中,因为它可以有任意的长度。另外,我们这种设计也意味着我们只需要对象的一份拷贝,比如这个加法 `7 + 7`, 现在常量表 `"numbers"`只需包含一个`[7]`。 你可能会想为什么会需要除了`ADD_TWO_VALUES`之外的指令。的确,对于我们两个数加法,这个例子是有点人为制作的意思。然而,这个指令却是建造更复杂程序的轮子。比如,就我们目前定义的三个指令,只要给出正确的指令组合,我们可以做三个数的加法,或者任意个数的加法。同时,栈提供了一个清晰的方法去跟踪解释器的状态,这为我们增长的复杂性提供了支持。 @@ -89,7 +89,7 @@ class Interpreter: self.stack.append(total) ``` -这三个方法完成了解释器所理解的三条指令。但解释器还需要一样东西:一个能把所有东西结合在一起并执行的方法。这个方法就叫做`run_code`, 它把我们前面定义的字典结构`what-to-execute`作为参数,循环执行里面的每条指令,如何指令有参数,处理参数,然后调用解释器对象中相应的方法。 +这三个方法完成了解释器所理解的三条指令。但解释器还需要一样东西:一个能把所有东西结合在一起并执行的方法。这个方法就叫做 `run_code`,它把我们前面定义的字典结构 `what-to-execute` 作为参数,循环执行里面的每条指令,如果指令有参数就处理参数,然后调用解释器对象中相应的方法。 ```python def run_code(self, what_to_execute): @@ -106,20 +106,20 @@ class Interpreter: self.PRINT_ANSWER() ``` -为了测试,我们创建一个解释器对象,然后用前面定义的 7 + 5 的指令集来调用`run_code`。 +为了测试,我们创建一个解释器对象,然后用前面定义的 7 + 5 的指令集来调用 `run_code`。 ```python interpreter = Interpreter() interpreter.run_code(what_to_execute) ``` -显然,它会输出12 +显然,它会输出12。 -尽管我们的解释器功能受限,但这个加法过程几乎和真正的Python解释器是一样的。这里,我们还有几点要注意。 +尽管我们的解释器功能十分受限,但这个过程几乎和真正的 Python 解释器处理加法是一样的。这里,我们还有几点要注意。 -首先,一些指令需要参数。在真正的Python bytecode中,大概有一半的指令有参数。像我们的例子一样,参数和指令打包在一起。注意_指令_的参数和传递给对应方法的参数是不同的。 +首先,一些指令需要参数。在真正的 Python 字节码当中,大概有一半的指令有参数。像我们的例子一样,参数和指令打包在一起。注意指令的参数和传递给对应方法的参数是不同的。 -第二,指令`ADD_TWO_VALUES`不需要任何参数,它从解释器栈中弹出所需的值。这正是以栈为基础的解释器的特点。 +第二,指令`ADD_TWO_VALUES`不需要任何参数,它从解释器栈中弹出所需的值。这正是以基于栈的解释器的特点。 记得我们说过只要给出合适的指令集,不需要对解释器做任何改变,我们就能做多个数的加法。考虑下面的指令集,你觉得会发生什么?如果你有一个合适的编译器,什么代码才能编译出下面的指令集? @@ -134,11 +134,11 @@ class Interpreter: "numbers": [7, 5, 8] } ``` -从这点出发,我们开始看到这种结构的可扩展性:我们可以通过向解释器对象增加方法来描述更多的操作(只要有一个编译器能为我们生成组织良好的指令集)。 +从这点出发,我们开始看到这种结构的可扩展性:我们可以通过向解释器对象增加方法来描述更多的操作(只要有一个编译器能为我们生成组织良好的指令集就行)。 -#### Variables +##### 变量 -接下来给我们的解释器增加变量的支持。我们需要一个保存变量值的指令,`STORE_NAME`;一个取变量值的指令`LOAD_NAME`;和一个变量到值的映射关系。目前,我们会忽略命名空间和作用域,所以我们可以把变量和值的映射直接存储在解释器对象中。最后,我们要保证`what_to_execute`除了一个常量列表,还要有个变量名字的列表。 +接下来给我们的解释器增加变量的支持。我们需要一个保存变量值的指令 `STORE_NAME`;一个取变量值的指令`LOAD_NAME`;和一个变量到值的映射关系。目前,我们会忽略命名空间和作用域,所以我们可以把变量和值的映射直接存储在解释器对象中。最后,我们要保证`what_to_execute`除了一个常量列表,还要有个变量名字的列表。 ```python >>> def s(): @@ -159,9 +159,9 @@ class Interpreter: "names": ["a", "b"] } ``` -我们的新的的实现在下面。为了跟踪哪名字绑定到那个值,我们在`__init__`方法中增加一个`environment`字典。我们也增加了`STORE_NAME`和`LOAD_NAME`方法,它们获得变量名,然后从`environment`字典中设置或取出这个变量值。 +我们的新的实现在下面。为了跟踪哪个名字绑定到哪个值,我们在`__init__`方法中增加一个`environment`字典。我们也增加了`STORE_NAME`和`LOAD_NAME`方法,它们获得变量名,然后从`environment`字典中设置或取出这个变量值。 -现在指令参数就有两个不同的意思,它可能是`numbers`列表的索引,也可能是`names`列表的索引。解释器通过检查所执行的指令就能知道是那种参数。而我们打破这种逻辑 ,把指令和它所用何种参数的映射关系放在另一个单独的方法中。 +现在指令的参数就有两个不同的意思,它可能是`numbers`列表的索引,也可能是`names`列表的索引。解释器通过检查所执行的指令就能知道是那种参数。而我们打破这种逻辑 ,把指令和它所用何种参数的映射关系放在另一个单独的方法中。 ```python class Interpreter: @@ -207,7 +207,7 @@ class Interpreter: self.LOAD_NAME(argument) ``` -仅仅五个指令,`run_code`这个方法已经开始变得冗长了。如果保持这种结构,那么每条指令都需要一个`if`分支。这里,我们要利用Python的动态方法查找。我们总会给一个称为`FOO`的指令定义一个名为`FOO`的方法,这样我们就可用Python的`getattr`函数在运行时动态查找方法,而不用这个大大的分支结构。`run_code`方法现在是这样: +仅仅五个指令,`run_code`这个方法已经开始变得冗长了。如果保持这种结构,那么每条指令都需要一个`if`分支。这里,我们要利用 Python 的动态方法查找。我们总会给一个称为`FOO`的指令定义一个名为`FOO`的方法,这样我们就可用 Python 的`getattr`函数在运行时动态查找方法,而不用这个大大的分支结构。`run_code`方法现在是这样: ```python def execute(self, what_to_execute): @@ -222,9 +222,9 @@ class Interpreter: bytecode_method(argument) ``` -## Real Python Bytecode +### 真实的 Python 字节码 -现在,放弃我们的小指令集,去看看真正的Python字节码。字节码的结构和我们的小解释器的指令集差不多,除了字节码用一个字节而不是一个名字来指示这条指令。为了理解它的结构,我们将考察一个函数的字节码。考虑下面这个例子: +现在,放弃我们的小指令集,去看看真正的 Python 字节码。字节码的结构和我们的小解释器的指令集差不多,除了字节码用一个字节而不是一个名字来代表这条指令。为了理解它的结构,我们将考察一个函数的字节码。考虑下面这个例子: ```python >>> def cond(): @@ -236,7 +236,7 @@ class Interpreter: ... ``` -Python在运行时会暴露一大批内部信息,并且我们可以通过REPL直接访问这些信息。对于函数对象`cond`,`cond.__code__`是与其关联的code object,而`cond.__code__.co_code`就是它的字节码。当你写Python代码时,你永远也不会想直接使用这些属性,但是这可以让我们做出各种恶作剧,同时也可以看看内部机制。 +Python 在运行时会暴露一大批内部信息,并且我们可以通过 REPL 直接访问这些信息。对于函数对象`cond`,`cond.__code__`是与其关联的代码对象,而`cond.__code__.co_code`就是它的字节码。当你写 Python 代码时,你永远也不会想直接使用这些属性,但是这可以让我们做出各种恶作剧,同时也可以看看内部机制。 ```python >>> cond.__code__.co_code # the bytecode as raw bytes @@ -247,9 +247,9 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0 100, 4, 0, 83, 100, 0, 0, 83] ``` -当我们直接输出这个字节码,它看起来完全无法理解 --- 唯一我们了解的是它是一串字节。很幸运,我们有一个很强大的工具可以用:Python标准库中的`dis`模块。 +当我们直接输出这个字节码,它看起来完全无法理解 —— 唯一我们了解的是它是一串字节。很幸运,我们有一个很强大的工具可以用:Python 标准库中的`dis`模块。 -`dis`是一个字节码反汇编器。反汇编器以为机器而写的底层代码作为输入,比如汇编代码和字节码,然后以人类可读的方式输出。当我们运行`dis.dis`, 它输出每个字节码的解释。 +`dis`是一个字节码反汇编器。反汇编器以为机器而写的底层代码作为输入,比如汇编代码和字节码,然后以人类可读的方式输出。当我们运行`dis.dis`,它输出每个字节码的解释。 ```python >>> dis.dis(cond) @@ -270,9 +270,9 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0 29 RETURN_VALUE ``` -这些都是什么意思?让我们以第一条指令`LOAD_CONST`为例子。第一列的数字(`2`)表示对应源代码的行数。第二列的数字是字节码的索引,告诉我们指令`LOAD_CONST`在0位置。第三列是指令本身对应的人类可读的名字。如果第四列存在,它表示指令的参数。如果第5列存在,它是一个关于参数是什么的提示。 +这些都是什么意思?让我们以第一条指令`LOAD_CONST`为例子。第一列的数字(`2`)表示对应源代码的行数。第二列的数字是字节码的索引,告诉我们指令`LOAD_CONST`在位置 0 。第三列是指令本身对应的人类可读的名字。如果第四列存在,它表示指令的参数。如果第五列存在,它是一个关于参数是什么的提示。 -考虑这个字节码的前几个字节:[100, 1, 0, 125, 0, 0]。这6个字节表示两条带参数的指令。我们可以使用`dis.opname`,一个字节到可读字符串的映射,来找到指令100和指令125代表是什么: +考虑这个字节码的前几个字节:[100, 1, 0, 125, 0, 0]。这 6 个字节表示两条带参数的指令。我们可以使用`dis.opname`,一个字节到可读字符串的映射,来找到指令 100 和指令 125 代表的是什么: ```python >>> dis.opname[100] @@ -281,11 +281,11 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0 'STORE_FAST' ``` -第二和第三个字节 --- 1 ,0 ---是`LOAD_CONST`的参数,第五和第六个字节 --- 0,0 --- 是`STORE_FAST`的参数。就像我们前面的小例子,`LOAD_CONST`需要知道的到哪去找常量,`STORE_FAST`需要找到名字。(Python的`LOAD_CONST`和我们小例子中的`LOAD_VALUE`一样,`LOAD_FAST`和`LOAD_NAME`一样)。所以这六个字节代表第一行源代码`x = 3`.(为什么用两个字节表示指令的参数?如果Python使用一个字节,每个code object你只能有256个常量/名字,而用两个字节,就增加到了256的平方,65536个)。 +第二和第三个字节 —— 1 、0 ——是`LOAD_CONST`的参数,第五和第六个字节 —— 0、0 —— 是`STORE_FAST`的参数。就像我们前面的小例子,`LOAD_CONST`需要知道的到哪去找常量,`STORE_FAST`需要知道要存储的名字。(Python 的`LOAD_CONST`和我们小例子中的`LOAD_VALUE`一样,`LOAD_FAST`和`LOAD_NAME`一样)。所以这六个字节代表第一行源代码`x = 3` (为什么用两个字节表示指令的参数?如果 Python 使用一个字节,每个代码对象你只能有 256 个常量/名字,而用两个字节,就增加到了 256 的平方,65536个)。 -### Conditionals and Loops +#### 条件语句与循环语句 -到目前为止,我们的解释器只能一条接着一条的执行指令。这有个问题,我们经常会想多次执行某个指令,或者在特定的条件下跳过它们。为了可以写循环和分支结构,解释器必须能够在指令中跳转。在某种程度上,Python在字节码中使用`GOTO`语句来处理循环和分支!让我们再看一个`cond`函数的反汇编结果: +到目前为止,我们的解释器只能一条接着一条的执行指令。这有个问题,我们经常会想多次执行某个指令,或者在特定的条件下跳过它们。为了可以写循环和分支结构,解释器必须能够在指令中跳转。在某种程度上,Python 在字节码中使用`GOTO`语句来处理循环和分支!让我们再看一个`cond`函数的反汇编结果: ```python >>> dis.dis(cond) @@ -306,11 +306,11 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0 29 RETURN_VALUE ``` -第三行的条件表达式`if x < 5`被编译成四条指令:`LOAD_FAST`, `LOAD_CONST`, `COMPARE_OP`和 `POP_JUMP_IF_FALSE`。`x < 5`对应加载`x`,加载5,比较这两个值。指令`POP_JUMP_IF_FALSE`完成`if`语句。这条指令把栈顶的值弹出,如果值为真,什么都不发生。如果值为假,解释器会跳转到另一条指令。 +第三行的条件表达式`if x < 5`被编译成四条指令:`LOAD_FAST`、 `LOAD_CONST`、 `COMPARE_OP`和 `POP_JUMP_IF_FALSE`。`x < 5`对应加载`x`、加载 5、比较这两个值。指令`POP_JUMP_IF_FALSE`完成这个`if`语句。这条指令把栈顶的值弹出,如果值为真,什么都不发生。如果值为假,解释器会跳转到另一条指令。 -这条将被加载的指令称为跳转目标,它作为指令`POP_JUMP`的参数。这里,跳转目标是22,索引为22的指令是`LOAD_CONST`,对应源码的第6行。(`dis`用`>>`标记跳转目标。)如果`X < 5`为假,解释器会忽略第四行(`return yes`),直接跳转到第6行(`return "no"`)。因此解释器通过跳转指令选择性的执行指令。 +这条将被加载的指令称为跳转目标,它作为指令`POP_JUMP`的参数。这里,跳转目标是 22,索引为 22 的指令是`LOAD_CONST`,对应源码的第 6 行。(`dis`用`>>`标记跳转目标。)如果`X < 5`为假,解释器会忽略第四行(`return yes`),直接跳转到第6行(`return "no"`)。因此解释器通过跳转指令选择性的执行指令。 -Python的循环也依赖于跳转。在下面的字节码中,`while x < 5`这一行产生了和`if x < 10`几乎一样的字节码。在这两种情况下,解释器都是先执行比较,然后执行`POP_JUMP_IF_FALSE`来控制下一条执行哪个指令。第四行的最后一条字节码`JUMP_ABSOLUT`(循环体结束的地方),让解释器返回到循环开始的第9条指令处。当 `x < 10`变为假,`POP_JUMP_IF_FALSE`会让解释器跳到循环的终止处,第34条指令。 +Python 的循环也依赖于跳转。在下面的字节码中,`while x < 5`这一行产生了和`if x < 10`几乎一样的字节码。在这两种情况下,解释器都是先执行比较,然后执行`POP_JUMP_IF_FALSE`来控制下一条执行哪个指令。第四行的最后一条字节码`JUMP_ABSOLUT`(循环体结束的地方),让解释器返回到循环开始的第 9 条指令处。当 `x < 10`变为假,`POP_JUMP_IF_FALSE`会让解释器跳到循环的终止处,第 34 条指令。 ```python >>> def loop(): @@ -340,23 +340,23 @@ Python的循环也依赖于跳转。在下面的字节码中,`while x < 5`这 38 RETURN_VALUE ``` -### Explore Bytecode +#### 探索字节码 我希望你用`dis.dis`来试试你自己写的函数。一些有趣的问题值得探索: -- 对解释器而言for循环和while循环有什么不同? +- 对解释器而言 for 循环和 while 循环有什么不同? - 能不能写出两个不同函数,却能产生相同的字节码? - `elif`是怎么工作的?列表推导呢? -## Frames +### 帧 -到目前为止,我们已经知道了Python虚拟机是一个栈机器。它能顺序执行指令,在指令间跳转,压入或弹出栈值。但是这和我们期望的解释器还有一定距离。在前面的那个例子中,最后一条指令是`RETURN_VALUE`,它和`return`语句相对应。但是它返回到哪里去呢? +到目前为止,我们已经知道了 Python 虚拟机是一个栈机器。它能顺序执行指令,在指令间跳转,压入或弹出栈值。但是这和我们期望的解释器还有一定距离。在前面的那个例子中,最后一条指令是`RETURN_VALUE`,它和`return`语句相对应。但是它返回到哪里去呢? -为了回答这个问题,我们必须再增加一层复杂性:frame。一个frame是一些信息的集合和代码的执行上下文。frames在Python代码执行时动态的创建和销毁。每个frame对应函数的一次调用。--- 所以每个frame只有一个code object与之关联,而一个code object可以有多个frame。比如你有一个函数递归的调用自己10次,这会产生11个frame,每次调用对应一个,再加上启动模块对应的一个frame。总的来说,Python程序的每个作用域有一个frame,比如,模块,函数,类。 +为了回答这个问题,我们必须再增加一层复杂性:帧(frame)。一个帧是一些信息的集合和代码的执行上下文。帧在 Python 代码执行时动态地创建和销毁。每个帧对应函数的一次调用 —— 所以每个帧只有一个代码对象与之关联,而一个代码对象可以有多个帧。比如你有一个函数递归的调用自己 10 次,这会产生 11 个帧,每次调用对应一个,再加上启动模块对应的一个帧。总的来说,Python 程序的每个作用域都有一个帧,比如,模块、函数、类定义。 -Frame存在于_调用栈_中,一个和我们之前讨论的完全不同的栈。(你最熟悉的栈就是调用栈,就是你经常看到的异常回溯,每个以"File 'program.py'"开始的回溯对应一个frame。)解释器在执行字节码时操作的栈,我们叫它_数据栈_。其实还有第三个栈,叫做_块栈_,用于特定的控制流块,比如循环和异常处理。调用栈中的每个frame都有它自己的数据栈和块栈。 +帧存在于调用栈(call stack)中,一个和我们之前讨论的完全不同的栈。(你最熟悉的栈就是调用栈,就是你经常看到的异常回溯,每个以"File 'program.py'"开始的回溯对应一个帧。)解释器在执行字节码时操作的栈,我们叫它数据栈(data stack)。其实还有第三个栈,叫做块栈(block stack),用于特定的控制流块,比如循环和异常处理。调用栈中的每个帧都有它自己的数据栈和块栈。 -让我们用一个具体的例子来说明。假设Python解释器执行到标记为3的地方。解释器正在`foo`函数的调用中,它接着调用`bar`。下面是frame调用栈,块栈和数据栈的示意图。我们感兴趣的是解释器先从最底下的`foo()`开始,接着执行`foo`的函数体,然后到达`bar`。 +让我们用一个具体的例子来说明一下。假设 Python 解释器执行到下面标记为 3 的地方。解释器正处于`foo`函数的调用中,它接着调用`bar`。下面是帧调用栈、块栈和数据栈的示意图。我们感兴趣的是解释器先从最底下的`foo()`开始,接着执行`foo`的函数体,然后到达`bar`。 ```python >>> def bar(y): @@ -372,32 +372,30 @@ Frame存在于_调用栈_中,一个和我们之前讨论的完全不同的栈 3 ``` -\aosafigure[240pt]{interpreter-images/interpreter-callstack.png}{The call stack}{500l.interpreter.callstack} +![调用栈](http://aosabook.org/en/500L/interpreter-images/interpreter-callstack.png) -现在,解释器在`bar`函数的调用中。调用栈中有3个frame:一个对应于模块层,一个对应函数`foo`,别一个对应函数`bar`。(\aosafigref{500l.interpreter.callstack}.)一旦`bar`返回,与它对应的frame就会从调用栈中弹出并丢弃。 +现在,解释器处于`bar`函数的调用中。调用栈中有 3 个帧:一个对应于模块层,一个对应函数`foo`,另一个对应函数`bar`。(见上图)一旦`bar`返回,与它对应的帧就会从调用栈中弹出并丢弃。 -字节码指令`RETURN_VALUE`告诉解释器在frame间传递一个值。首先,它把位于调用栈栈顶的frame中的数据栈的栈顶值弹出。然后把整个frame弹出丢弃。最后把这个值压到下一个frame的数据栈中。 +字节码指令`RETURN_VALUE`告诉解释器在帧之间传递一个值。首先,它把位于调用栈栈顶的帧中的数据栈的栈顶值弹出。然后把整个帧弹出丢弃。最后把这个值压到下一个帧的数据栈中。 -当Ned Batchelder和我在写Byterun时,很长一段时间我们的实现中一直有个重大的错误。我们整个虚拟机中只有一个数据栈,而不是每个frame都有一个。我们写了很多测试代码,同时在Byterun和真正的Python上运行,希望得到一致结果。我们几乎通过了所有测试,只有一样东西不能通过,那就是生成器。最后,通过仔细的阅读CPython的源码,我们发现了错误所在[^thanks]。把数据栈移到每个frame就解决了这个问题。 +当 Ned Batchelder 和我在写 Byterun 时,很长一段时间我们的实现中一直有个重大的错误。我们整个虚拟机中只有一个数据栈,而不是每个帧都有一个。我们写了很多测试代码,同时在 Byterun 和真正的 Python 上运行,希望得到一致结果。我们几乎通过了所有测试,只有一样东西不能通过,那就是生成器(generators)。最后,通过仔细的阅读 CPython 的源码,我们发现了错误所在(感谢 Michael Arntzenius 对这个 bug 的洞悉)。把数据栈移到每个帧就解决了这个问题。 -[^thanks]: 感谢 Michael Arntzenius 对这个bug的洞悉。 +回头在看看这个 bug,我惊讶的发现 Python 真的很少依赖于每个帧有一个数据栈这个特性。在 Python 中几乎所有的操作都会清空数据栈,所以所有的帧公用一个数据栈是没问题的。在上面的例子中,当`bar`执行完后,它的数据栈为空。即使`foo`公用这一个栈,它的值也不会受影响。然而,对应生成器,它的一个关键的特点是它能暂停一个帧的执行,返回到其他的帧,一段时间后它能返回到原来的帧,并以它离开时的相同状态继续执行。 -回头在看看这个bug,我惊讶的发现Python真的很少依赖于每个frame有一个数据栈这个特性。在Python中几乎所有的操作都会清空数据栈,所以所有的frame公用一个数据栈是没问题的。在上面的例子中,当`bar`执行完后,它的数据栈为空。即使`foo`公用这一个栈,它的值也不会受影响。然而,对应生成器,一个关键的特点是它能暂停一个frame的执行,返回到其他的frame,一段时间后它能返回到原来的frame,并以它离开时的相同状态继续执行。 +### Byterun -## Byterun +现在我们有足够的 Python 解释器的知识背景去考察 Byterun。 -现在我们有足够的Python解释器的知识背景去考察Byterun。 +Byterun 中有四种对象。 -Byterun中有四种对象。 +- `VirtualMachine`类,它管理高层结构,尤其是帧调用栈,并包含了指令到操作的映射。这是一个比前面`Inteprter`对象更复杂的版本。 +- `Frame`类,每个`Frame`类都有一个代码对象,并且管理着其他一些必要的状态位,尤其是全局和局部命名空间、指向调用它的整的指针和最后执行的字节码指令。 +- `Function`类,它被用来代替真正的 Python 函数。回想一下,调用函数时会创建一个新的帧。我们自己实现了`Function`,以便我们控制新的`Frame`的创建。 +- `Block`类,它只是包装了块的 3 个属性。(块的细节不是解释器的核心,我们不会花时间在它身上,把它列在这里,是因为 Byterun 需要它。) -- `VirtualMachine`类,它管理高层结构,frame调用栈,指令到操作的映射。这是一个比前面`Inteprter`对象更复杂的版本。 -- `Frame`类,每个`Frame`类都有一个code object,并且管理者其他一些必要的状态信息,全局和局部命名空间,指向调用它的frame的指针和最后执行的字节码指令。 -- `Function`类,它被用来代替真正的Python函数。回想一下,调用函数时会创建一个新的frame。我们自己实现`Function`,所以我们控制新frame的创建。 -- `Block`类,它只是包装了block的3个属性。(block的细节不是解释器的核心,我们不会花时间在它身上,把它列在这里,是因为Byterun需要它。) +#### `VirtualMachine` 类 -### The `VirtualMachine` Class - -程序运行时只有一个`VirtualMachine`被创建,因为我们只有一个解释器。`VirtualMachine`保存调用栈,异常状态,在frame中传递的返回值。它的入口点是`run_code`方法,它以编译后的code object为参数,以创建一个frame为开始,然后运行这个frame。这个frame可能再创建出新的frame;调用栈随着程序的运行增长缩短。当第一个frame返回时,执行结束。 +每次程序运行时只会创建一个`VirtualMachine`实例,因为我们只有一个 Python 解释器。`VirtualMachine` 保存调用栈、异常状态、在帧之间传递的返回值。它的入口点是`run_code`方法,它以编译后的代码对象为参数,以创建一个帧为开始,然后运行这个帧。这个帧可能再创建出新的帧;调用栈随着程序的运行而增长和缩短。当第一个帧返回时,执行结束。 ```python class VirtualMachineError(Exception): @@ -418,9 +416,9 @@ class VirtualMachine(object): ``` -### The `Frame` Class +#### `Frame` 类 -接下来,我们来写`Frame`对象。frame是一个属性的集合,它没有任何方法。前面提到过,这些属性包括由编译器生成的code object;局部,全局和内置命名空间;前一个frame的引用;一个数据栈;一个块栈;最后执行的指令指针。(对于内置命名空间我们需要多做一点工作,Python在不同模块中对这个命名空间有不同的处理;但这个细节对我们的虚拟机不重要。) +接下来,我们来写`Frame`对象。帧是一个属性的集合,它没有任何方法。前面提到过,这些属性包括由编译器生成的代码对象;局部、全局和内置命名空间;前一个帧的引用;一个数据栈;一个块栈;最后执行的指令指针。(对于内置命名空间我们需要多做一点工作,Python 在不同模块中对这个命名空间有不同的处理;但这个细节对我们的虚拟机不重要。) ```python class Frame(object): @@ -441,7 +439,7 @@ class Frame(object): self.block_stack = [] ``` -接着,我们在虚拟机中增加对frame的操作。这有3个帮助函数:一个创建新的frame的方法,和压栈和出栈的方法。第四个函数,`run_frame`,完成执行frame的主要工作,待会我们再讨论这个方法。 +接着,我们在虚拟机中增加对帧的操作。这有 3 个帮助函数:一个创建新的帧的方法(它负责为新的帧找到名字空间),和压栈和出栈的方法。第四个函数,`run_frame`,完成执行帧的主要工作,待会我们再讨论这个方法。 ```python class VirtualMachine(object): @@ -481,9 +479,9 @@ class VirtualMachine(object): # we'll come back to this shortly ``` -### The `Function` Class +#### `Function` 类 -`Function`的实现有点扭曲,但是大部分的细节对理解解释器不重要。重要的是当调用函数时 --- `__call__`方法被调用 --- 它创建一个新的`Frame`并运行它。 +`Function`的实现有点曲折,但是大部分的细节对理解解释器不重要。重要的是当调用函数时 —— 即调用 `__call__`方法 —— 它创建一个新的`Frame`并运行它。 ```python class Function(object): @@ -534,7 +532,7 @@ def make_cell(value): return fn.__closure__[0] ``` -接着,回到`VirtualMachine`对象,我们对数据栈的操作也增加一些帮助方法。字节码操作的栈总是在当前frame的数据栈。这些帮助函数让我们能实现`POP_TOP`,`LOAD_FAST`字节码,并且让其他操作栈的指令可读性更高。 +接着,回到`VirtualMachine`对象,我们对数据栈的操作也增加一些帮助方法。字节码操作的栈总是在当前帧的数据栈。这些帮助函数让我们的`POP_TOP`、`LOAD_FAST`以及其他操作栈的指令的实现可读性更高。 ```python class VirtualMachine(object): @@ -562,11 +560,11 @@ class VirtualMachine(object): return [] ``` -在我们运行frame之前,我们还需两个方法。 +在我们运行帧之前,我们还需两个方法。 -第一个方法,`parse_byte_and_args`,以一个字节码为输入,先检查它是否有参数,如果有,就解析它的参数。这个方法同时也更新frame的`last_instruction`属性,它指向最后执行的指令。一条没有参数的指令只有一个字节长度,而有参数的字节有3个字节长。参数的意义依赖于指令是什么。比如,前面说过,指令`POP_JUMP_IF_FALSE`,它的参数指的是跳转目标。`BUILD_LIST`, 它的参数是列表的个数。`LOAD_CONST`,它的参数是常量的索引。 +第一个方法,`parse_byte_and_args` 以一个字节码为输入,先检查它是否有参数,如果有,就解析它的参数。这个方法同时也更新帧的`last_instruction`属性,它指向最后执行的指令。一条没有参数的指令只有一个字节长度,而有参数的字节有3个字节长。参数的意义依赖于指令是什么。比如,前面说过,指令`POP_JUMP_IF_FALSE`,它的参数指的是跳转目标。`BUILD_LIST`,它的参数是列表的个数。`LOAD_CONST`,它的参数是常量的索引。 -一些指令用简单的数字作为参数。对于另一些,虚拟机需要一点努力去发现它含意。标准库中的`dis`模块中有一个备忘单,它解释什么参数有什么意思,这让我们的代码更加简洁。比如,列表`dis.hasname`告诉我们`LOAD_NAME`, `IMPORT_NAME`,`LOAD_GLOBAL`,以及另外的9个指令都有同样的意思:名字列表的索引。 +一些指令用简单的数字作为参数。对于另一些,虚拟机需要一点努力去发现它含意。标准库中的`dis`模块中有一个备忘单,它解释什么参数有什么意思,这让我们的代码更加简洁。比如,列表`dis.hasname`告诉我们`LOAD_NAME`、 `IMPORT_NAME`、`LOAD_GLOBAL`,以及另外的 9 个指令的参数都有同样的意义:对于这些指令,它们的参数代表了代码对象中的名字列表的索引。 ```python class VirtualMachine(object): @@ -600,8 +598,7 @@ class VirtualMachine(object): return byte_name, argument ``` -下一个方法是`dispatch`,它查看给定的指令并执行相应的操作。在CPython中,这个分派函数用一个巨大的switch语句实现,有超过1500行的代码。幸运的是,我们用的是Python,我们的代码会简洁的多。我们会为每一个字节码名字定义一个方法,然后用`getattr`来查找。就像我们前面的小解释器一样,如果一条指令叫做`FOO_BAR`,那么它对应的方法就是`byte_FOO_BAR`。现在,我们先把这些方法当做一个黑盒子。每个指令方法都会返回`None`或者一个字符串`why`,有些情况下虚拟机需要这个额外`why`信息。这些指令方法的返回值,仅作为解释器状态的内部指示,千万不要和执行frame的返回值相混淆。 - +下一个方法是`dispatch`,它查找给定的指令并执行相应的操作。在 CPython 中,这个分派函数用一个巨大的 switch 语句实现,有超过 1500 行的代码。幸运的是,我们用的是 Python,我们的代码会简洁的多。我们会为每一个字节码名字定义一个方法,然后用`getattr`来查找。就像我们前面的小解释器一样,如果一条指令叫做`FOO_BAR`,那么它对应的方法就是`byte_FOO_BAR`。现在,我们先把这些方法当做一个黑盒子。每个指令方法都会返回`None`或者一个字符串`why`,有些情况下虚拟机需要这个额外`why`信息。这些指令方法的返回值,仅作为解释器状态的内部指示,千万不要和执行帧的返回值相混淆。 ```python class VirtualMachine(object): @@ -662,13 +659,13 @@ class VirtualMachine(object): return self.return_value ``` -### The `Block` Class +#### `Block` 类 在我们完成每个字节码方法前,我们简单的讨论一下块。一个块被用于某种控制流,特别是异常处理和循环。它负责保证当操作完成后数据栈处于正确的状态。比如,在一个循环中,一个特殊的迭代器会存在栈中,当循环完成时它从栈中弹出。解释器需要检查循环仍在继续还是已经停止。 -为了跟踪这些额外的信息,解释器设置了一个标志来指示它的状态。我们用一个变量`why`实现这个标志,它可以是`None`或者是下面几个字符串这一,`"continue"`, `"break"`,`"excption"`,`return`。他们指示对块栈和数据栈进行什么操作。回到我们迭代器的例子,如果块栈的栈顶是一个`loop`块,`why`是`continue`,迭代器就因该保存在数据栈上,不是如果`why`是`break`,迭代器就会被弹出。 +为了跟踪这些额外的信息,解释器设置了一个标志来指示它的状态。我们用一个变量`why`实现这个标志,它可以是`None`或者是下面几个字符串之一:`"continue"`、`"break"`、`"excption"`、`return`。它们指示对块栈和数据栈进行什么操作。回到我们迭代器的例子,如果块栈的栈顶是一个`loop`块,`why`的代码是`continue`,迭代器就应该保存在数据栈上,而如果`why`是`break`,迭代器就会被弹出。 -块操作的细节比较精细,我们不会花时间在这上面,但是有兴趣的读者值得仔细的看看。 +块操作的细节比这个还要繁琐,我们不会花时间在这上面,但是有兴趣的读者值得仔细的看看。 ```python Block = collections.namedtuple("Block", "type, handler, stack_height") @@ -737,9 +734,9 @@ class VirtualMachine(object): return why ``` -## The Instructions +### 指令 -剩下了的就是完成那些指令方法了:`byte_LOAD_FAST`,`byte_BINARY_MODULO`等等。而这些指令的实现并不是很有趣,这里我们只展示了一小部分,完整的实现在这儿https://github.com/nedbat/byterun。(足够执行我们前面所述的所有代码了。) +剩下了的就是完成那些指令方法了:`byte_LOAD_FAST`、`byte_BINARY_MODULO`等等。而这些指令的实现并不是很有趣,这里我们只展示了一小部分,完整的实现[在 GitHub 上](https://github.com/nedbat/byterun)。(这里包括的指令足够执行我们前面所述的所有代码了。) ```python class VirtualMachine(object): @@ -926,11 +923,11 @@ class VirtualMachine(object): return "return" ``` -## Dynamic Typing: What the Compiler Doesn't Know +### 动态类型:编译器不知道它是什么 -你可能听过Python是一种动态语言 --- 是它是动态类型的。在我们建造解释器的过程中,已经透露出这样的信息。 +你可能听过 Python 是一种动态语言 —— 它是动态类型的。在我们建造解释器的过程中,已经透露出这样的信息。 -动态的一个意思是很多工作在运行时完成。前面我们看到Python的编译器没有很多关于代码真正做什么的信息。举个例子,考虑下面这个简单的函数`mod`。它取两个参数,返回它们的模运算值。从它的字节码中,我们看到变量`a`和`b`首先被加载,然后字节码`BINAY_MODULO`完成这个模运算。 +动态的一个意思是很多工作是在运行时完成的。前面我们看到 Python 的编译器没有很多关于代码真正做什么的信息。举个例子,考虑下面这个简单的函数`mod`。它取两个参数,返回它们的模运算值。从它的字节码中,我们看到变量`a`和`b`首先被加载,然后字节码`BINAY_MODULO`完成这个模运算。 ```python >>> def mod(a, b): @@ -944,25 +941,25 @@ class VirtualMachine(object): 4 ``` -计算19 % 5得4,--- 一点也不奇怪。如果我们用不同类的参数呢? +计算 19 % 5 得4,—— 一点也不奇怪。如果我们用不同类的参数呢? ```python >>> mod("by%sde", "teco") 'bytecode' ``` -刚才发生了什么?你可能见过这样的语法,格式化字符串。 +刚才发生了什么?你可能在其它地方见过这样的语法,格式化字符串。 ``` >>> print("by%sde" % "teco") bytecode ``` -用符号`%`去格式化字符串会调用字节码`BUNARY_MODULO`.它取栈顶的两个值求模,不管这两个值是字符串,数字或是你自己定义的类的实例。字节码在函数编译时生成(或者说,函数定义时)相同的字节码会用于不同类的参数。 +用符号`%`去格式化字符串会调用字节码`BUNARY_MODULO`。它取栈顶的两个值求模,不管这两个值是字符串、数字或是你自己定义的类的实例。字节码在函数编译时生成(或者说,函数定义时)相同的字节码会用于不同类的参数。 -Python的编译器关于字节码的功能知道的很少。而取决于解释器来决定`BINAYR_MODULO`应用于什么类型的对象并完成正确的操作。这就是为什么Python被描述为_动态类型_:直到运行前你不必知道这个函数参数的类型。相反,在一个静态类型语言中,程序员需要告诉编译器参数的类型是什么(或者编译器自己推断出参数的类型。) +Python 的编译器关于字节码的功能知道的很少,而取决于解释器来决定`BINAYR_MODULO`应用于什么类型的对象并完成正确的操作。这就是为什么 Python 被描述为动态类型(dynamically typed):直到运行前你不必知道这个函数参数的类型。相反,在一个静态类型语言中,程序员需要告诉编译器参数的类型是什么(或者编译器自己推断出参数的类型。) -编译器的无知是优化Python的一个挑战 --- 只看字节码,而不真正运行它,你就不知道每条字节码在干什么!你可以定义一个类,实现`__mod__`方法,当你对这个类的实例使用`%`时,Python就会自动调用这个方法。所以,`BINARY_MODULO`其实可以运行任何代码。 +编译器的无知是优化 Python 的一个挑战 —— 只看字节码,而不真正运行它,你就不知道每条字节码在干什么!你可以定义一个类,实现`__mod__`方法,当你对这个类的实例使用`%`时,Python 就会自动调用这个方法。所以,`BINARY_MODULO`其实可以运行任何代码。 看看下面的代码,第一个`a % b`看起来没有用。 @@ -972,25 +969,23 @@ def mod(a,b): return a %b ``` -不幸的是,对这段代码进行静态分析 --- 不运行它 --- 不能确定第一个`a % b`没有做任何事。用 `%`调用`__mod__`可能会写一个文件,或是和程序的其他部分交互,或者其他任何可以在Python中完成的事。很难优化一个你不知道它会做什么的函数。在Russell Power和Alex Rubinsteyn的优秀论文中写道,“我们可以用多快的速度解释Python?”,他们说,“在普遍缺乏类型信息下,每条指令必须被看作一个`INVOKE_ARBITRARY_METHOD`。” +不幸的是,对这段代码进行静态分析 —— 不运行它 —— 不能确定第一个`a % b`没有做任何事。用 `%`调用`__mod__`可能会写一个文件,或是和程序的其他部分交互,或者其他任何可以在 Python 中完成的事。很难优化一个你不知道它会做什么的函数。在 Russell Power 和 Alex Rubinsteyn 的优秀论文中写道,“我们可以用多快的速度解释 Python?”,他们说,“在普遍缺乏类型信息下,每条指令必须被看作一个`INVOKE_ARBITRARY_METHOD`。” -## Conclusion +### 总结 -Byterun是一个比CPython容易理解的简洁的Python解释器。Byterun复制了CPython的主要结构:一个基于栈的指令集称为字节码,它们顺序执行或在指令间跳转,向栈中压入和从中弹出数据。解释器随着函数和生成器的调用和返回,动态的创建,销毁frame,并在frame间跳转。Byterun也有着和真正解释器一样的限制:因为Python使用动态类型,解释器必须在运行时决定指令的正确行为。 +Byterun 是一个比 CPython 容易理解的简洁的 Python 解释器。Byterun 复制了 CPython 的主要结构:一个基于栈的解释器对称之为字节码的指令集进行操作,它们顺序执行或在指令间跳转,向栈中压入和从中弹出数据。解释器随着函数和生成器的调用和返回,动态的创建、销毁帧,并在帧之间跳转。Byterun 也有着和真正解释器一样的限制:因为 Python 使用动态类型,解释器必须在运行时决定指令的正确行为。 -我鼓励你去反汇编你的程序,然后用Byterun来运行。你很快会发现这个缩短版的Byterun所没有实现的指令。完整的实现在https://github.com/nedbat/byterun,或者仔细阅读真正的CPython解释器`ceval.c`,你也可以实现自己的解释器! +我鼓励你去反汇编你的程序,然后用 Byterun 来运行。你很快会发现这个缩短版的 Byterun 所没有实现的指令。完整的实现在 https://github.com/nedbat/byterun ,或者你可以仔细阅读真正的 CPython 解释器`ceval.c`,你也可以实现自己的解释器! -## Acknowledgements +### 致谢 -Thanks to Ned Batchelder for originating this project and guiding my contributions, Michael Arntzenius for his help debugging the code and editing the prose, Leta Montopoli for her edits, and the entire Recurse Center community for their support and interest. Any errors are my own. +感谢 Ned Batchelder 发起这个项目并引导我的贡献,感谢 Michael Arntzenius 帮助调试代码和这篇文章的修订,感谢 Leta Montopoli 的修订,以及感谢整个 Recurse Center 社区的支持和鼓励。所有的不足全是我自己没搞好。 -------------------------------------- -via:http://aosabook.org/en/500L/a-python-interpreter-written-in-python.html - -作者: Allison Kaptur Allison +via: http://aosabook.org/en/500L/a-python-interpreter-written-in-python.html +作者: Allison Kaptur 译者:[qingyunha](https://github.com/qingyunha) - -校对:[校对者ID](https://github.com/校对者ID) +校对:[wxy](https://github.com/wxy) 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出