在P2.7脚python2.7 输出不换行一个200Hz的方波的Timer0中断服务函数源代码

单片机:利用单片机P2.7接口,输出周期为800微秒的方波,流程_百度知道
单片机:利用单片机P2.7接口,输出周期为800微秒的方波,流程
单片机:利用单片机P2.7接口,输出周期为800微秒的方波,流程谢谢了!
我有更好的答案
ORG 0000HLJMP MAINORG 000BHLJMP T0ISRORG 0030HMAIN:MOV TMOD,#02HMOV TH0,#(256-200)MOV TL0,#(256-200)SETB TR0SETB EASETB ET0CLR 00HSJMP $T0ISR:CPL 00HJB 00H,T0ECPL P2.7T0E:RETIEND
高级工程师
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。我姐结婚,我二伯撰文,大伯朗读,他们都是老派知识分子,解放前的大学生,念的是朗朗上口,特别好听,众宾客都惊呆了。当时觉得真是高大上。&br&原文如下(为保护隐私将新人姓名隐掉):&br&&br&唯 公元xxxx年x月x日,农历岁次xx x月x日 愚伯等 谨以珍酌时馐恭贺贤侄女新婚志喜&br&&br&盖闻&br&易正乾坤 夫妇为人伦之始&br&诗歌周召 婚姻乃王化之源&br&是以&br&鸣凤锵锵 卜其昌于五世&br&夭桃灼灼 歌好合于百年&br&今x君xx
世泽贻芳 才誉素着&br&xx女士 名门淑媛 绣阁名姝&br&允称璧合珠联之妙&br&克臻琴谐瑟调之欢&br&增来鸿案之光&br& 结此凤仪之好&br&爰于此良辰美景 欢言嘉礼 共协唱随&br&入洞房而喜溢 花烛辉煌&br&睹华屋之欢腾 笙歌迭奏&br&欣此时宜室宜家 烂门庭而有耀&br&卜他日而昌而炽 庆瓜瓞兮绵长&br&用志燕喜以抒忱 为尽长乐未央之颂&br&特贺鸳盟而书牒 愿敦二好无尤之文&br&寅筮吉辰 行合卺之礼&br&伏愿&br&百年偕老 永结琴瑟之欢&br&五尽其昌 早协熊罴之庆&br&&br&伯父xx率各全家&br&恭贺新禧&br&&br&&br&那场面帅呆了酷毙了。&br&不用谢。
我姐结婚,我二伯撰文,大伯朗读,他们都是老派知识分子,解放前的大学生,念的是朗朗上口,特别好听,众宾客都惊呆了。当时觉得真是高大上。 原文如下(为保护隐私将新人姓名隐掉): 唯 公元xxxx年x月x日,农历岁次xx x月x日 愚伯等 谨以珍酌时馐恭贺贤侄…
&figure&&img src=&https://pic1.zhimg.com/v2-3db09a8bc759_b.jpg& data-rawwidth=&1152& data-rawheight=&578& class=&origin_image zh-lightbox-thumb& width=&1152& data-original=&https://pic1.zhimg.com/v2-3db09a8bc759_r.jpg&&&/figure&&blockquote&&b&原文地址:&/b&&a href=&https://link.zhihu.com/?target=https%3A//www.dataquest.io/blog/jupyter-notebook-tutorial/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jupyter Notebook for Beginners: A Tutorial&/a&&br&&b&原文作者:&/b&&a href=&https://link.zhihu.com/?target=https%3A//www.dataquest.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&dataquest&/a&&br&&b&译文出自:&/b&&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&掘金翻译计划&/a&&br&&b&本文永久链接:&/b&&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner/blob/master/TODO1/jupyter-notebook-tutorial.md& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/xitu/gold-mi&/span&&span class=&invisible&&ner/blob/master/TODO1/jupyter-notebook-tutorial.md&/span&&span class=&ellipsis&&&/span&&/a&&br&&b&译者:&/b&&a href=&https://link.zhihu.com/?target=https%3A//github.com/SergeyChang& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SergeyChang&/a&&br&校对者:&a href=&https://link.zhihu.com/?target=https%3A//github.com/sunhaokk& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&sunhaokk&/a&,&a href=&https://link.zhihu.com/?target=https%3A//github.com/sisibeloved& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&陆尘&/a&&/blockquote&&hr&&p&Jupyter Notebook 是一个非常强大的工具,常用于交互式地开发和展示数据科学项目。它将代码和它的输出集成到一个文档中,并且结合了可视的叙述性文本、数学方程和其他丰富的媒体。它直观的工作流促进了迭代和快速的开发,使得 notebook 在当代数据科学、分析和越来越多的科学研究中越来越受欢迎。最重要的是,作为&a href=&https://link.zhihu.com/?target=https%3A//jupyter.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&开源项目&/a&的一部分,它们是完全免费的。&/p&&p&Jupyter 项目是早期 IPython Notebook 的继承者,它在 2010 年首次作为原型发布。尽管在 Jupyter Notebook 中可以使用许多不同的编程语言,但本文将重点介绍 Python,因为在 Jupyter Notebook 中 python 是最常见的。&/p&&p&为了充分理解本教程,你应该熟悉编程,特别是 Python 和 &a href=&https://link.zhihu.com/?target=https%3A//pandas.pydata.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&pandas&/a&(译者注:Pandas 是python的一个数据分析包)。也就是说,如果你有编程经验,这篇文章中的 Python 不会太陌生,而 pandas 也是容易理解的。Jupyter Notebooks 也可以作为一个灵活的平台来运行 pandas 甚至是 Python,这将在这篇文章中体现。&/p&&p&我们将会:&/p&&ul&&li&介绍一些安装 Jupyter 和创建你的第一个 notebook 的基本知识。&/li&&li&深入钻研,学习所有重要的术语。&/li&&li&探索笔记是如何轻松地在网上共享和发布。事实上,这篇文章就是一个 Jupyter notebook!这里的一切都是在 Jupyter notebook 环境中编写的,而你正在以只读的形式查看它。&/li&&/ul&&h2&Jupyter Notebook 数据分析实例&/h2&&p&我们将通过一个样本分析,来回答一个真实的问题,这样你就可以看到一个 notebook 的工作流是如何使任务直观地完成的,当我们分享给其他人时也可以让其他人更好地理解。&/p&&p&假设你是一名数据分析师,你的任务是弄清楚美国最大公司的利润变化历史。你会发现自从 1955 年第一次发表这个名单以来,已有超过 50 年的财富 500 强企业的数据集,这些数据都是从&a href=&https://link.zhihu.com/?target=http%3A//archive.fortune.com/magazines/fortune/fortune500_archive/full/2005/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&《财富》&/a&的公共档案中收集来的。我们已经创建了一个可用数据的 CSV 文件(你可以在&a href=&https://link.zhihu.com/?target=https%3A//www.dataquest.io/blog/large_files/fortune500.csv& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&获取它)。&/p&&p&正如我们将要演示的,Jupyter Notebooks 非常适合这项调查。首先,让我们安装 Jupyter。&/p&&h2&安装&/h2&&p&初学者开始使用 Jupyter Notebooks 的最简单方法是安装 &a href=&https://link.zhihu.com/?target=https%3A//anaconda.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Anaconda&/a&。Anaconda 是最广泛使用的用于数据科学的 Python 发行版,并且预装了所有常用的库和工具。除了 Jupyter 之外,Anaconda 中还封装了一些 Python 库,包括 &a href=&https://link.zhihu.com/?target=http%3A//www.numpy.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&NumPy&/a&,&a href=&https://link.zhihu.com/?target=https%3A//pandas.pydata.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&pandas&/a& 和 &a href=&https://link.zhihu.com/?target=https%3A//matplotlib.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Matplotlib&/a&,并且这&a href=&https://link.zhihu.com/?target=https%3A//docs.anaconda.com/anaconda/packages/pkg-docs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&完整的1000+列表&/a&是详尽的。这使你可以在自己完备的数据科学研讨会中运行,而不需要管理无数的安装包或担心依赖项和特定操作系统的安装问题。&/p&&p&安装 Anaconda:&/p&&ol&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.anaconda.com/download/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&下载&/a&支持 Python 3 (就不用 Python 2.7 了)的最新版本 Anaconda。&/li&&li&按照下载页面或可执行文件中的说明安装 Anaconda。&/li&&/ol&&p&如果你是已经安装了 Python 的更高级的用户,并且更喜欢手动管理你的软件包,那么你可以使用pip:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&pip3 install jupyter
&/code&&/pre&&/div&&h2&创建你的第一个 Notebook&/h2&&p&在本节中,我们将看到如何运行和保存 notebooks,熟悉它们的结构,并理解接口。我们将会熟悉一些核心术语,这些术语将引导你对如何使用 Jupyter notebooks 进行实际的理解,并为下一节做铺垫,该部分将通过示例数据分析,并将我们在这里学到的所有东西带到生活中。&/p&&h2&运行 Jupyter&/h2&&p&在 Windows 上,你可以通过将 Anaconda 快捷方式添加到你的开始菜单来运行 Jupyter,它将在你的默认网页浏览器中打开一个新的标签,看起来就像下面的截图一样。&/p&&p&&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-cefcd4f98c4e6f6f65736_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1429& data-rawheight=&450& class=&origin_image zh-lightbox-thumb& width=&1429& data-original=&https://pic2.zhimg.com/v2-cefcd4f98c4e6f6f65736_r.jpg&&&/figure&&p&&br&&/p&&p&这是 Notebook Dashboard,专门用于管理 Jupyter Notebooks。把它看作是探索,编辑和创建 notebooks 的启动面板。你可以把它看作是探索、编辑和创造你的 notebook 的发射台。&/p&&p&请注意,仪表板将只允许您访问 Jupyter 启动目录中包含的文件和子文件夹;但是,启动目录是&a href=&https://link.zhihu.com/?target=https%3A//stackoverflow.com/q/687& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&可以更改的&/a&。还可以通过输入 &code&jupyter notebook&/code& 命令在任何系统上启动指示板(或在Unix系统上的终端);在这种情况下,当前工作目录将是启动目录。&/p&&p&聪明的读者可能已经注意到,仪表板的 URL 类似于 &code&http://localhost:8888/tree&/code&。Localhost 不是一个网站,而是表示从你的&i&本地&/i&机器(你自己的计算机)中服务的内容。Jupyter notebook 和仪表板都是 web 应用程序,Jupyter 启动了一个本地的 Python 服务器,将这些应用程序提供给你的 web 浏览器,使其从根本上独立于平台,并打开了更容易在 web 上共享的大门。&/p&&p&仪表板的界面大部分是不言自明的 —— 尽管我们稍后会简要介绍它。我们还在等什么?浏览到你想要创建你的第一个 notebook 的文件夹,点击右上角的 &code&New&/code& 下拉按钮,选择 &code&Python 3&/code& (或者你喜欢的版本)。&/p&&p&&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f3d73d86d3a56c4e5530fbcb3adabffc_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&260& data-rawheight=&240& class=&content_image& width=&260&&&/figure&&p&&br&&/p&&p&我们马上能看到成果了!你的第一个 Jupyter Notebook 将在新标签页打开 - 每个 notebook 使用它自己的标签,因为你可以同时打开多个 notebook。如果您切换回仪表板,您将看到新文件 &code&Untitled&/code& 。你应该看到一些绿色的文字告诉 notebook 正在运行。&/p&&h2&什么是 ipynb 文件?&/h2&&p&理解这个文件到底是什么是很有用的。每一个 &code&.ipynb&/code& 文件是一个文本文件,它以一种名为 &a href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/JSON& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JSON&/a& 的格式描述你的 notebook 的内容。每个单元格及其内容,包括已被转换成文本字符串的图像附件,都与一些&a href=&https://link.zhihu.com/?target=https%3A//ipython.org/ipython-doc/3/notebook/nbformat.html%23metadata& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&元数据&/a&一起列出。你可以自己编辑这个 -- 如果你知道你在做什么! -- 通过在 notebook 的菜单栏中选择 &Edit & Edit Notebook Metadata&。&/p&&p&你还可以通过在仪表板上的控件中选择 &code&Edit&/code& 来查看你的 notebook 文件的内容,但是重要的是可以;除了好奇之外没有理由这样做,除非你真的知道你在做什么。&/p&&h2&notebook 的接口&/h2&&p&既然你面前有一个打开的 notebook,它的界面就不会看起来完全陌生;毕竟,Jupyter 实际上只是一个高级的文字处理器。为什么不看一看?查看菜单以了解它,尤其是花点时间浏览命令选项板(这是带键盘图标的小按钮(或 &code&Ctrl + Shift + P&/code&))下滚动命令列表。&/p&&p&&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-68bb1fea9d871f4c8ce9b8dd7fb6e290_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1439& data-rawheight=&205& class=&origin_image zh-lightbox-thumb& width=&1439& data-original=&https://pic3.zhimg.com/v2-68bb1fea9d871f4c8ce9b8dd7fb6e290_r.jpg&&&/figure&&p&&br&&/p&&p&您应该注意到两个非常重要的术语,这对您来说可能是全新的:&i&单元格&/i&和&i&内核&/i&。它们是理解 Jupyter 和区分 Jupyter 不只是一个文字处理器的关键。幸运的是,这些概念并不难理解。&/p&&ul&&li&内核是一个“计算引擎”,它执行一个 notebook 文档中包含的代码。&/li&&li&单元格是一个容器,用于装载在 notebook 中显示的文本或是会被 notebook 内核执行的代码。&/li&&/ul&&h2&单元格&/h2&&p&稍后我们再讨论内核,在这之前我们先来了解一下单元格。单元格构成一个笔记本的主体。在上面一节的新建的 notebook 屏幕截图中,带有绿色轮廓的盒子是一个空的单元格。我们将介绍两种主要的单元格类型:&/p&&ul&&li&&b&代码单元&/b&包含要在内核中执行的代码,并在下面显示它的输出。&/li&&li&&b&Markdown 单元&/b&包含使用 Markdown 格式化的文本,并在运行时显示其输出。&/li&&/ul&&p&新的 notebook 中的第一个单元总是一个代码单元。让我们用一个经典的 hello world 示例来测试它。输入 &code&print('Hello World!')&/code& 到单元格中,点击上面工具栏中的 run 按钮,或者按下 Ctrl + Enter 键。结果应该是这样的:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&print('Hello World!')
Hello World!
&/code&&/pre&&/div&&p&当你运行这个单元格时,它的输出将会显示在它的下面,而它左边的标签将会从 &code&In [ ]&/code& 变为 &code&In [1]&/code&。代码单元的输出也是文档的一部分,这就是为什么你可以在本文中看到它的原因。你总是可以区分代码和 Markdown 单元,因为代码单元格在左边有标签,而 Markdown 单元没有。标签的“In”部分仅仅是“输入”的缩写,而标签号表示在内核上执行单元格时的顺序 —— 在这种情况下,单元格被第一个执行。再次运行单元格,标签将更改为 &code&In[2]&/code&,因为此时单元格是在内核上运行的第二个单元格。这让我们在接下来对内核的深入将非常有用。&/p&&p&从菜单栏中,单击&i&插入&/i&并选择&i&在下方插入单元格&/i&,创建你新的代码单元,并尝试下面的代码,看看会发生什么。你注意到有什么不同吗?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import time
time.sleep(3)
&/code&&/pre&&/div&&p&这个单元不产生任何输出,但执行需要 3 秒。请注意,Jupyter 将标签更改为 &code&In[*]&/code& 来表示单元格当前正在运行。&/p&&p&一般来说,单元格的输出来自于单元执行过程中指定打印的任何文本数据,以及单元格中最后一行的值,无论是单独变量,函数调用还是其他内容。例如:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&def say_hello(recipient):
return 'Hello, {}!'.format(recipient)
say_hello('Tim')
'Hello, Tim!'
&/code&&/pre&&/div&&p&你会发现自己经常在自己的项目中使用它,以后我们会看到更多。&/p&&h2&键盘快捷键&/h2&&p&在运行单元格时,你可能经常看到它们的边框变成了蓝色,而在编辑的时候它是绿色的。总是有一个“活动”单元格突出显示其当前模式,绿色表示“编辑模式”,蓝色表示“命令模式”。&/p&&p&到目前为止,我们已经看到了如何使用 &code&Ctrl + Enter&/code& 来运行单元格,但是还有很多。键盘快捷键是 Jupyter 环境中非常流行的一个方面,因为它们促进了快速的基于单元格的工作流。许多这些都是在命令模式下可以在活动单元上执行的操作。&/p&&p&下面,你会发现一些 Jupyter 的键盘快捷键列表。你可能不会马上熟悉它们,但是这份清单应该让你对这些快捷键有了了解。&/p&&ul&&li&在编辑和命令模式之间切换,分别使用 &code&Esc&/code& 和 &code&Enter&/code&。&/li&&li&在命令行模式下:&br&&/li&&ul&&li&用 &code&Up&/code& 和 &code&Down&/code& 键向上和向下滚动你的单元格。&/li&&li&按 &code&A&/code& 或 &code&B&/code& 在活动单元上方或下方插入一个新单元。&/li&&li&&code&M&/code& 将会将活动单元格转换为 Markdown 单元格。&/li&&li&&code&Y&/code& 将激活的单元格设置为一个代码单元格。&/li&&li&&code&D + D&/code&(按两次 &code&D&/code&)将删除活动单元格。&/li&&li&&code&Z&/code&将撤销单元格删除。
* 按住 &code&Shift&/code&,同时按 &code&Up&/code& 或 &code&Down&/code& ,一次选择多个单元格。&br&&/li&&ul&&li&选择了 multple,&code&Shift + M&/code& 将合并你的选择。&/li&&/ul&&/ul&&/ul&&p&&br&&/p&&p&&br&&/p&&ul&&li&&code&Ctrl + Shift + -&/code&,在编辑模式下,将在光标处拆分活动单元格。&/li&&li&你也可以在你的单元格的左边用 &code&Shift + Click&/code& 来选择它们。&/li&&/ul&&p&你可以在自己的 notebook 上试试这些。一旦你有了尝试,创建一个新的 Markdown 单元,我们将学习如何在我们的 notebook 中格式化文本。&/p&&h2&Markdown&/h2&&p&&a href=&https://link.zhihu.com/?target=https%3A//www.markdownguide.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Markdown&/a& 是一种轻量级的、易于学习的标记语言,用于格式化纯文本。它的语法与 HTML 标记有一对一的对应关系,所以这里的一些经验是有用的,但绝对不是先决条件。请记住,这篇文章是在一个 Jupyter notebook 上写的,所以你所看到的所有的叙述文本和图片都是在 Markdown 完成的。让我们用一个简单的例子来介绍基础知识。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&# 这是一级标题。
## 这是一个二级标题。
这是一些构成段落的纯文本。
通过 **粗体** 和 __bold__ ,或 *斜体* 和 _italic_ 添加重点。
段落必须用空行隔开。
* 有时我们想要包含列表。
* 可以缩进。
1. 列表也可以编号。
2. 有序列表。
[有可能包括超链接](https://www.example.com)
内联代码使用单个倒引号:`foo()`,代码块使用三个倒引号:
或可由4个空格组成:
最后,添加图片也很简单:![Alt](https://www.example.com/image.jpg)
&/code&&/pre&&/div&&p&当附加图像时,你有三个选项:&/p&&ul&&li&使用一个在 web 上的图像的 URL。&/li&&li&使用一个与你的 notebook 一起维护的本地 URL,例如在同一个 git 仓库中。&/li&&li&通过 &Edit & Insert Image& 添加附件;这将把图像转换成字符串并存储在你的 notebook 中的 &code&.ipynb&/code& 文件。&/li&&li&注意这将使你的 &code&.ipynb&/code& 的文件更大!&/li&&/ul&&p&Markdown 有很多细节,特别是在超链接的时候,也可以简单地包括纯 HTML。一旦你发现自己突破了上述基础的限制,你可以参考 Markdown 创造者 John Gruber 的&a href=&https://link.zhihu.com/?target=https%3A//daringfireball.net/projects/markdown/syntax& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&官方指南&/a&。&/p&&h2&内核&/h2&&p&每个 notebook 后台都运行一个内核。当你运行一个代码单元时,该代码在内核中执行,任何输出都会返回到要显示的单元格。在单元格间切换时内核的状态保持不变 —— 它与文档有关,而不是单个的单元格。&/p&&p&例如,如果你在一个单元中导入库或声明变量,那么它们将在另一个单元中可用。通过这种方式,你可以将 notebook 文档看作是与脚本文件相当的,除了它是多媒体。让我们试着去感受一下。首先,我们将导入一个 Python 包并定义一个函数。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import numpy as np
def square(x):
return x * x
&/code&&/pre&&/div&&p&一旦我们执行了上面的单元格,我们就可以在任何其他单元中引用 &code&np&/code&和 &code&square&/code&。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&x = np.random.randint(1, 10)
y = square(x)
print('%d squared is %d' % (x, y))
1 squared is 1
&/code&&/pre&&/div&&p&不管你的 notebook 里的单元格顺序如何,这都是可行的。你可以自己试一下,让我们再把变量打印出来。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&print('Is %d squared is %d?' % (x, y))
Is 1 squared is 1?
&/code&&/pre&&/div&&p&答案毫无疑问。让我们尝试改变 &code&y&/code&。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&y = 10
&/code&&/pre&&/div&&p&如果我们再次运行包含 &code&print&/code& 语句的单元格,你认为会发生什么?我们得到的结果是 &code&Is 4 squared is 10?&/code&!&/p&&p&大多数情况下,你的 notebook 上的工作流将会从上到下,但是返回上文做一些改变是很正常的。在这种情况下,每个单元的左侧的执行顺序,例如 &code&In [6]&/code&,将让你知道你的任何单元格是否有陈旧的输出。如果你想要重置一些东西,从内核菜单中有几个非常有用的选项:&/p&&ul&&li&重启:重新启动内核,从而清除定义的所有变量。&/li&&li&重启和清除输出:与上面一样,但也将擦除显示在您的代码单元格下面的输出。&/li&&li&重启和运行所有:和上面一样,但也会运行你的所有单元,从第一个到最后。&/li&&/ul&&p&如果你的内核一直在计算中,但你希望停止它,你可以选择 &code&Interupt&/code& 选项。&/p&&h2&选择一个内核&/h2&&p&你可能已经注意到,Jupyter 提供了更改内核的选项,实际上有许多不同的选项可供选择。当你通过选择 Python 版本从仪表板中创建一个新的笔记时,你实际上是在选择使用哪个内核。&/p&&p&不仅有不同版本的 Python 的内核,还有&a href=&https://link.zhihu.com/?target=https%3A//github.com/jupyter/jupyter/jupyter/wiki/jupyter-kernel& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&(超过 100 种语言)&/a&,包括 Java 、C ,甚至 Fortran。数据科学家可能特别感兴趣的是 &a href=&https://link.zhihu.com/?target=https%3A//irkernel.github.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&R&/a& 和 &a href=&https://link.zhihu.com/?target=https%3A//github.com/JuliaLang/IJulia.jl& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Julia&/a&,以及 &a href=&https://link.zhihu.com/?target=https%3A//github.com/imatlab/imatlab& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&imatlab&/a& 和 &a href=&https://link.zhihu.com/?target=https%3A//github.com/calysto/matlab_kernel& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Calysto MATLAB内核&/a& 。&a href=&https://link.zhihu.com/?target=https%3A//github.com/vatlab/SOS& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SoS 内核&/a&在一个 notebook 中提供多语言支持。每个内核都有自己的安装指令,但可能需要您在计算机上运行一些命令。&/p&&h2&实例分析&/h2&&p&现在我们已经看了一个 Jupyter Notebook,是时候看看它们在实践中使用了,这应该会让你更清楚地了解它们为什么那么受欢迎。现在是时候开始使用前面提到的财富 500 数据集了。请记住,我们的目标是了解美国最大公司的利润在历史上是如何变化的。&/p&&p&值得注意的是,每个人都会有自己的喜好和风格,但是一般原则仍然适用,如果你愿意,你可以在自己的 notebook 上跟随这一段,这也给了你自由发挥空间。&/p&&h2&命名你的 notebook&/h2&&p&在开始编写项目之前,你可能想要给它一个有意义的名称。也许有点让人困惑,你不能从 Notebook 的应用程序中命名或重命名你的 notebook,而必须使用仪表盘或你的文件浏览器来重命名 &code&.ipynb&/code& 文件。我们将返回到仪表板,以重命名你之前创建的文件,它将有默认的 notebook 的文件名是 &code&Untitled.ipynb&/code& 。&/p&&p&你不能在 notebook 运行时重命名它,所以你首先要关闭它。最简单的方法就是从 notebook 菜单中选择 “File & Close and Halt”。但是,您也可以通过在笔记本应用程序内 “Kernel & Shutdown” 或在仪表板中选择 notebook 并点击 “Shutdown” (见下图)来关闭内核。&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e607fefde1bfc9c9ab1b3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1434& data-rawheight=&224& class=&origin_image zh-lightbox-thumb& width=&1434& data-original=&https://pic4.zhimg.com/v2-e607fefde1bfc9c9ab1b3_r.jpg&&&/figure&&p&&br&&/p&&p&然后你可以选择你的 notebook,并在仪表板控件中点击 “Rename”。&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-c39cd8f1f11c296acce27e69b4b9eb16_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1434& data-rawheight=&220& class=&origin_image zh-lightbox-thumb& width=&1434& data-original=&https://pic4.zhimg.com/v2-c39cd8f1f11c296acce27e69b4b9eb16_r.jpg&&&/figure&&p&&br&&/p&&p&注意,在你的浏览器中关闭笔记的标签页将不会像在传统的应用程序中关闭文档的方式一样关闭你的 notebook。notebook 的内核将继续在后台运行,需要在真正“关闭”之前停止运行 —— 不过如果你不小心关掉了你的标签或浏览器,这就很方便了!如果内核被关闭,你可以关闭该选项卡,而不用担心它是否还在运行。&/p&&p&如果你给你的 notebook 起了名字,打开它,我们就可以开始实践了。&/p&&h2&设置&/h2&&p&通常一开始就使用一个专门用于导入和设置的代码单元,因此如果你选择添加或更改任何内容,你可以简单地编辑和重新运行该单元,而不会产生任何副作用。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style=&darkgrid&)
&/code&&/pre&&/div&&p&我们导入 &a href=&https://link.zhihu.com/?target=https%3A//pandas.pydata.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&pandas&/a& 来处理我们的数据,&a href=&https://link.zhihu.com/?target=https%3A//matplotlib.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Matplotlib&/a& 绘制图表,&a href=&https://link.zhihu.com/?target=https%3A//seabornpydata.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Seaborn&/a& 使我们的图表更美。导入 &a href=&https://link.zhihu.com/?target=http%3A//www.numpy.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&NumPy&/a& 也是很常见的,但是在这种情况下,虽然我们使用的是 pandas,但我们不需要显式地使用它。第一行不是 Python 命令,而是使用一种叫做行魔法的东西来指示 Jupyter 捕获 Matplotlib 图并在单元输出中呈现它们;这是超出本文范围的一系列高级特性之一。&/p&&p&让我们来加载数据。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&df = pd.read_csv('fortune500.csv')
&/code&&/pre&&/div&&p&在单个单元格中这样做也是明智的,因为我们需要在任何时候重新加载它。&/p&&h2&保存和检查点&/h2&&p&现在我们已经开始了,最好的做法是定期存储。按 &code&Ctrl + S&/code& 键可以通过调用“保存和检查点”命令来保存你的 notebook,但是这个检查点又是什么呢?&/p&&p&每当你创建一个新的 notebook 时,都会创建一个检查点文件以及你的 notebook 文件;它将位于你保存位置的隐藏子目录中称作 &code&.ipynb_checkpoints&/code&,也是一个 &code&.ipynb&/code& 文件。默认情况下,Jupyter 将每隔 120 秒自动保存你的 notebook,而不会改变你的主 notebook 文件。当你“保存和检查点”时,notebook 和检查点文件都将被更新。因此,检查点使你能够在发生意外事件时恢复未保存的工作。你可以通过 “File & Revert to Checkpoint“ 从菜单恢复到检查点。&/p&&h2&调查我们的数据集&/h2&&p&我们正在稳步前进!我们的笔记已经被安全保存,我们将数据集 &code&df&/code& 加载到最常用的 pandas 数据结构中,这被称为 &code&DataFrame&/code& ,看起来就像一张表格。那我们的数据集会是怎样的?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&df.head()
&/code&&/pre&&/div&&p&&b&YearRankCompanyRevenue (in millions)Profit (in millions)&/b&019551General Motors552Exxon Mobil19553U.S. Steel19554General Electric19555Esmark&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&df.tail()
&/code&&/pre&&/div&&p&&b&YearRankCompanyRevenue (in millions)Profit (in millions)&/b&Wm. Wrigley Jr.Peabody EnergyWendy's InternationalKindred HealthcareCincinnati Financial&/p&&p&看上去不错。我们有需要的列,每一行对应一个公司一年的财务数据。&/p&&p&让我们重命名这些列,以便稍后引用它们。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&df.columns = ['year', 'rank', 'company', 'revenue', 'profit']
&/code&&/pre&&/div&&p&接下来,我们需要探索我们的数据集,它是否完整? pandas 是按预期读的吗?缺少值吗?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&len(df)
&/code&&/pre&&/div&&p&好吧,看起来不错 —— 从 1955 年到 2005 年,每年都有 500 行。&/p&&p&让我们检查我们的数据集是否如我们预期的那样被导入。一个简单的检查就是查看数据类型(或 dtypes)是否被正确地解释。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&df.dtypes
dtype: object
&/code&&/pre&&/div&&p&看起来利润栏有点问题 —— 我们希望它像收入栏一样是 &code&float64&/code&。这表明它可能包含一些非整数值,所以让我们看一看。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&non_numberic_profits = df.profit.str.contains('[^0-9.-]')
df.loc[non_numberic_profits].head()
&/code&&/pre&&/div&&p&&b&yearrankcompanyrevenueprofit&/b&Norton135.0N.A.Schlitz Brewing100.0N.A.Pacific Vegetable Oil97.9N.A.Liebmann Breweries96.0N.A.Minneapolis-Moline77.4N.A.&/p&&p&就像我们猜测的那样!其中一些值是字符串,用于表示丢失的数据。还有其他缺失的值么?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&set(df.profit[non_numberic_profits])
&/code&&/pre&&/div&&p&这很容易解释,但是我们应该怎么做呢?这取决于缺失了多少个值。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&len(df.profit[non_numberic_profits])
&/code&&/pre&&/div&&p&它只是我们数据集的一小部分,虽然不是完全无关紧要,因为它仍然在 1.5% 左右。如果包含 N.A. 的行是简单地、均匀地按年分布的,那最简单的解决方案就是删除它们。所以让我们浏览一下分布。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&bin_sizes, _, _ = plt.hist(df.year[non_numberic_profits], bins=range())
&/code&&/pre&&/div&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-a14c94fd07a5b65a0ddb5a722f6a0ac8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&367& data-rawheight=&249& class=&content_image& width=&367&&&/figure&&p&&br&&/p&&p&粗略地看,我们可以看到,在一年中无效值最多的情况也小于 25,并且由于每年有 500 个数据点,删除这些值在最糟糕的年份中只占不到 4% 的数据。事实上,除了 90 年代的激增,大多数年份的缺失值还不到峰值的一半。为了我们的目的,假设这是可以接受的,然后移除这些行。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&df = df.loc[~non_numberic_profits]
df.profit = df.profit.apply(pd.to_numeric)
&/code&&/pre&&/div&&p&我们看看有没有生效。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&len(df)
dtype: object
&/code&&/pre&&/div&&p&不错!我们已经完成了数据集的设置。&/p&&p&如果你要将 notebook 做成一个报告,你可以不使用我们创建的研究的单元格,包括这里的演示使用 notebook 的工作流,合并相关单元格(请参阅下面的高级功能部分)并创建一个数据集设置单元格。这意味着如果我们把我们的数据放在别处,我们可以重新运行安装单元来恢复它。&/p&&h2&使用 matplotlib 进行绘图&/h2&&p&接下来,我们可以通过计算年平均利润来解决这个问题。我们不妨把收入也画出来,所以首先我们可以定义一些变量和一种方法来减少我们的代码。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&group_by_year = df.loc[:, ['year', 'revenue', 'profit']].groupby('year')
avgs = group_by_year.mean()
x = avgs.index
y1 = avgs.profit
def plot(x, y, ax, title, y_label):
ax.set_title(title)
ax.set_ylabel(y_label)
ax.plot(x, y)
ax.margins(x=0, y=0)
&/code&&/pre&&/div&&p&现在让我们开始画图。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&fig, ax = plt.subplots()
plot(x, y1, ax, 'Increase in mean Fortune 500 company profits from 1955 to 2005', 'Profit (millions)')
&/code&&/pre&&/div&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-ddda6bcca3cdf1a0216ee_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&399& data-rawheight=&261& class=&content_image& width=&399&&&/figure&&p&&br&&/p&&p&它看起来像一个指数,但它有一些大的凹陷。它们一定是对应于&a href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Early_1990s_recession& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&上世纪 90 年代初的经济衰退&/a&和 &a href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Dot-com_bubble& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&互联网泡沫&/a&。在数据中能看到这一点非常有趣。但为什么每次经济衰退后,利润都能恢复到更高的水平呢?&/p&&p&也许收入能告诉我们更多。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&y2 = avgs.revenue
fig, ax = plt.subplots()
plot(x, y2, ax, 'Increase in mean Fortune 500 company revenues from 1955 to 2005', 'Revenue (millions)')
&/code&&/pre&&/div&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-59fce9c607f39cc01d93_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&413& data-rawheight=&261& class=&content_image& width=&413&&&/figure&&p&&br&&/p&&p&这为故事增添了另一面。收入几乎没有受到严重打击,财务部门的会计工作做得很好。&/p&&p&借助 &a href=&https://link.zhihu.com/?target=https%3A//stackoverflow.com/a/687& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Stack Overflow&/a& 上的帮助,我们可以用 +/- 它们的标准偏移来叠加这些图。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&def plot_with_std(x, y, stds, ax, title, y_label):
ax.fill_between(x, y - stds, y + stds, alpha=0.2)
plot(x, y, ax, title, y_label)
fig, (ax1, ax2) = plt.subplots(ncols=2)
title = 'Increase in mean and std Fortune 500 company %s from 1955 to 2005'
stds1 = group_by_year.std().profit.as_matrix()
stds2 = group_by_year.std().revenue.as_matrix()
plot_with_std(x, y1.as_matrix(), stds1, ax1, title % 'profits', 'Profit (millions)')
plot_with_std(x, y2.as_matrix(), stds2, ax2, title % 'revenues', 'Revenue (millions)')
fig.set_size_inches(14, 4)
fig.tight_layout()
&/code&&/pre&&/div&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-865ca09f4d4a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&974& data-rawheight=&280& class=&origin_image zh-lightbox-thumb& width=&974& data-original=&https://pic1.zhimg.com/v2-865ca09f4d4a_r.jpg&&&/figure&&p&&br&&/p&&p&这是惊人的,标准偏差是巨大的。一些财富 500 强的公司赚了数十亿,而另一些公司却损失了数十亿美元,而且随着这些年来利润的增长,风险也在增加。也许有些公司比其他公司表现更好;前 10% 的利润是否或多或少会比最低的10%稳定一些?&/p&&p&接下来我们有很多问题可以看,很容易看到在 notebook 上的工作流程是如何与自己的思维过程相匹配的,所以现在是时候为这个例子画上句号了。这一流程帮助我们在无需切换应用程序的情况下轻松地研究我们的数据集,并且我们的工作可以立即共享和重现。如果我们希望为特定的目标人群创建一个更简洁的报告,我们可以通过合并单元和删除中间代码来快速重构我们的工作。&/p&&h2&分享你的 notebook&/h2&&p&当人们谈论分享他们的 notebook 时,他们通常会考虑两种模式。大多数情况下,个人共享其工作的最终结果,就像本文本身一样,这意味着共享非交互式的、预渲染的版本的 notebook;然而,也可以在 notebook 上借助诸如 &a href=&https://link.zhihu.com/?target=https%3A//git-scm.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Git&/a& 这样的辅助版本控制系统进行协作。&/p&&p&也就是说,&a href=&https://link.zhihu.com/?target=https%3A//mybinder.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&有一些&/a&新兴的&a href=&https://link.zhihu.com/?target=https%3A//kyso.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&公司&/a&在 web 上提供了在云中运行交互式 Jupyter Notebook 的能力。&/p&&h2&在你分享之前&/h2&&p&当你导出或保存它时,共享的 notebook 将会以被导出或保存的那一刻的状态显示,包括所有代码单元的输出。因此,为了确保你的 notebook 是共享的,你可以在分享之前采取一些步骤:&/p&&ol&&li&点击 &Cell & All Output & Clear&&/li&&li&点击 &Kernel & Restart & Run All&&/li&&li&等待您的代码单元完成执行,并检查它们是否按预期执行。&/li&&/ol&&p&这将确保你的 notebook 不包含中间输出,不包含陈旧的状态,并在共享时按顺序执行。&/p&&h2&导出你的 notebook&/h2&&p&Jupyter 内置支持导出 HTML 和 PDF 以及其他几种格式,你可以在 &code&File & Download As&/code& 菜单下找到。如果你希望与一个小型的私有组共享你的 notebook,这个功能很可能是你所需要的。事实上,许多学术机构的研究人员都有一些公共或内部的网络空间,因为你可以将一个 notebook 导出到一个 HTML 文件中,Jupyter notebook 可以成为他们与同行分享成果的一种特别方便的方式。&/p&&p&但是,如果共享导出的文件并不能让你满意,那么还有一些更直接的非常流行的共享 &code&.ipynb&/code& 文件到网上的方法。&/p&&h2&GitHub&/h2&&p&截止到 2018 年初,GitHub 上的公共 notebook 数量超过了 180 万,它无疑是最受欢迎的与世界分享 Jupyter 项目的独立平台。GitHub 已经集成了对 &code&.ipynb&/code& 的文件渲染的支持,你可以直接将其存储在其网站的仓库和 gists 中。如果你还不知道,&a href=&https://link.zhihu.com/?target=https%3A//github.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub&/a& 是一个代码托管平台,用于为使用 &a href=&https://link.zhihu.com/?target=https%3A//git-scm.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Git&/a& 创建的存储库进行版本控制和协作。你需要创建一个帐户来使用他们的服务,同时 Github 标准帐户是免费的。&/p&&p&当你有了 GitHub 账户,在 GitHub 上共享一个 notebook 最简单的方法甚至都不需要 Git。自 2008 年以来, GitHub 为托管和共享代码片段提供了Gist 服务,每个代码段都有自己的存储库。使用 Gists 共享一个 notebook:&/p&&ol&&li&登录并且浏览 &a href=&https://link.zhihu.com/?target=https%3A//gist.github.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&gist.github.com&/a&。&/li&&li&用文件编辑器打开 &code&.ipynb&/code& 文件, 全选并且拷贝里面的 JSON 。&/li&&li&将笔记的 JSON 粘贴到中 gist 中。&/li&&li&给你的 Gist 命名, 记得添加 &code&.iypnb&/code& 后缀,否则不能正常工作。&/li&&li&点击 &Create secret gist&或者 &Create public gist.&&/li&&/ol&&p&这看起来应该是这样的:&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-ba9328bbee731c056d94de810af6b92e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&514& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic4.zhimg.com/v2-ba9328bbee731c056d94de810af6b92e_r.jpg&&&/figure&&p&&br&&/p&&p&如果你创建了一个公共的 Gist,你现在就可以和任何人分享它的 URL,其他人将能够 &a href=&https://link.zhihu.com/?target=https%3A//help.github.com/articles/forkingand-cloning-gists/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&fork 和 clone&/a& 你的工作。&/p&&p&创建自己的 Git 存储库并在 GitHub 上共享,这超出了本教程的范围,但是 &a href=&https://link.zhihu.com/?target=https%3A//guides.github.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub 提供了大量的指南&/a&可供你参考。&/p&&p&对于那些使用 git 的人来说,一个额外的技巧是在 &code&.gitignore&/code& 中为 Jupyter 创建的 &code&.ipynb_checkpoints&/code& 目录&a href=&https://link.zhihu.com/?target=https%3A//stackoverflow.com/q/687& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&添加例外&/a&,因为我们不需要将检查点文件提交给到仓库。&/p&&p&从 2015 年起,NBViewer 每个星期都会渲染&a href=&https://link.zhihu.com/?target=https%3A//blog.jupyter.org/renderingnotebooks-ongithub-f7ac& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&成千上万的 notebook&/a&,它已然成了最受欢迎的 notebook 渲染器。如果你已经在某个地方把你的 Jupyter Notebook 放在网上,无论是 GitHub 还是其他地方,NBViewer 都可以读取你的 notebook,并提供一个可共享的 URL。作为项目 Jupyter 的一部分提供的免费服务,你可以在 &a href=&https://link.zhihu.com/?target=https%3A//nbview.jupyter.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&nbview.jupyter.org&/a& 找到相关服务。&/p&&p&最初是在 GitHub 的 Jupyter Notebook 集成之前开发的,NBViewer 允许任何人输入 URL、Gist ID 或 &code&GitHub username/repo/filename&/code&,并将其作为网页呈现。一个 Gist 的 ID 是其 URL 末尾唯一的数字;例如,在 &code&https://gist.github.com/username/e0bf417e89e1de&/code& 中最后一个反斜杠后的字符串。如果你输入了 &code&GitHub username/repo/filename&/code& ,你将看到一个最小的文件浏览器,它允许你访问用户的仓库及其内容。&/p&&p&NBViewer 显示的 notebook 的 URL 是基于正在渲染的 notebook 的 URL 的并且不会改变,所以你可以和任何人分享它,只要原始文件保持在线 —— NBViewer 不会缓存文件很长时间。&/p&&h2&结语&/h2&&p&从基础知识入手,我们已经掌握了 Jupyter Notebook 的工作流程,深入研究了IPython 的更多高级功能,并最终学会如何与朋友、同事和世界分享我们的工作。我们从一个笔记上完成了这一切!&/p&&p&可以看到,notebook 是如何通过减少上下文切换和在项目中模拟自然的思维发展的方式来提高工作经验的。Jupyter Notebook。Jupyter Notebook 的功能也应该是显而易见的,我们已经介绍了大量的资源,让你开始在自己的项目中探索更高级的特性。&/p&&p&如果你想为自己的 Notebooks 提供更多的灵感,Jupyter 已经整理好了(&a href=&https://link.zhihu.com/?target=https%3A//github.com/jupyter/jupyter/wiki/A-gallery-of-interesting-Jupyter-Notebooks& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&一个有趣的 Jupyter Notebook 图库&/a&),你可能会发现它有帮助,并且你会发现 &a href=&https://link.zhihu.com/?target=https%3A//nbviewer.jupyter.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Nbviewer 的主页&/a&链接到一些真正的高质量笔记本的例子。也可以查看我们的 &a href=&https://link.zhihu.com/?target=https%3A//www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jupyter Notebooks 提示列表&/a&。&/p&&blockquote&想了解更多关于 Jupyter Notebooks 的知识吗?我们有&a href=&https://link.zhihu.com/?target=https%3A//www.dataquest.io/m/207/guided-project%25253A-using-jupyter-notebook& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&一个有指导的项目&/a&,你可能会感兴趣。&/blockquote&&hr&&blockquote&&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&掘金翻译计划&/a& 是一个翻译优质互联网技术文章的社区,文章来源为 &a href=&https://link.zhihu.com/?target=https%3A//juejin.im/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&掘金&/a& 上的英文分享文章。内容覆盖 &a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23android& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Android&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23ios& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&iOS&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23%25E5%E7%25AB%25AF& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&前端&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23%25E5%E7%25AB%25AF& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&后端&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23%25E5%258C%25BA%25E5%259D%%2593%25BE& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&区块链&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23%25E4%25BA%25A7%25E5%& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&产品&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23%25E8%25AE%25BE%25E8%25AE%25A1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&设计&/a&、&a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner%23%25E4%25BA%25BA%25E5%25B7%25A5%25E6%2599%25BA%25E8%2583%25BD& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&人工智能&/a&等领域,想要查看更多优质译文请持续关注 &a href=&https://link.zhihu.com/?target=https%3A//github.com/xitu/gold-miner& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&掘金翻译计划&/a&、&a href=&https://link.zhihu.com/?target=http%3A//weibo.com/juejinfanyi& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&官方微博&/a&、&a href=&https://zhuanlan.zhihu.com/juejinfanyi& class=&internal&&知乎专栏&/a&。&/blockquote&
原文地址: 原文作者: 译文出自: 本文永久链接: 译者: 校对者:,Jupyter Notebook 是一个非常强大的工具,常用于交互式地开发和展示数据…
&p&很难一概而论。&/p&&p&不过我觉得很多同学会有一种倾向:不观察数据特点,硬套机器学习模型,期待一下就能得到特别好的结果,一劳永逸。这种偷懒的做法,反而往往导致尝试很多不同模型无功而返,事倍功半。原因在于,即使一个模型的确有可能在这个任务上有效,大概率也会因为代码bug或预处理/细节处理不当而效果不佳。如果既不观察数据又不观察实验结果进行有针对性调试,浅尝辄止,就会误以为这个模型不适用,反而会误导后面的研究路线。&/p&&p&很多同学说,实验代码我反复检查好几遍,感觉不会有错。我的感受是,如果你心里已经预设这个代码没错,那就永远找不出错来。很多错误需要通过反复观察中间结果才能找到。&/p&&p&所以,即使实验做不出效果,那也要拼命找到没有效果的原因,而不是轻易换模型,不然很容易陷入下个模型也做不出效果的怪圈。&/p&
很难一概而论。不过我觉得很多同学会有一种倾向:不观察数据特点,硬套机器学习模型,期待一下就能得到特别好的结果,一劳永逸。这种偷懒的做法,反而往往导致尝试很多不同模型无功而返,事倍功半。原因在于,即使一个模型的确有可能在这个任务上有效,大概…
&p&作者:&a href=&https://link.zhihu.com/?target=https%3A//www.nowcoder.com/profile/1561797& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Re_born&/a&&/p&&p&来源:牛客网&/p&&p&&br&&/p&&p&&b&其实我面试的时候C++基本很少问及,不过还是要有扎实的基础。以下是自己的一份总结,比较适合自己,所以根据你们自己的需求拿走我的笔记,如有错误也请提出我好更改。回馈牛客网,谢谢。&/b&&/p&&p&&br&&/p&&p&&b&1.深拷贝和浅拷贝&/b&&/p&&p&深拷贝(Memberwise copy semantics)是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。&/p&&p&浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。&/p&&p&浅拷贝在类里面有指针成员的情况下只会复制指针的地址,会导致两个成员指针指向同一块内存,这样在要是分别delete释放时就会出现问题,因此需要用深拷贝。&/p&&p&&br&&/p&&p&&b&2.delete this&/b&&/p&&p&在类的成员函数中能不能调用delete this?答案是肯定的,能调用,而且很多老一点的库都有这种代码。假设这个成员函数名字叫release,而delete this就在这个release方法中被调用,那么这个对象在调用release方法后,还能进行其他操作,如调用该对象的其他方法么?答案仍然是肯定 的,调用release之后还能调用其他的方法,但是有个前提:被调用的方法不涉及这个对象的数据成员和虚函数。说到这里,相信大家都能明白为什么会这样了。&/p&&p&&br&&/p&&p&根本原因在于delete操作符的功能和类对象的内存模型。当一个类对象声明时,系统会为其分配内存空间。在类对象的内存空间中,只有数据成员和虚函数表指针,并不包含代码内容,类的成员函数单独放在代码段中。在调用成员函数时,隐含传递一个this指针,让成员函数知道当前是哪个对象在调用它。当调用delete this时,类对象的内存空间被释放。在delete this之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。&/p&&p&为什么是不可预期的问题?delete this之后不是释放了类对象的内存空间了么,那么这段内存应该已经还给系统,不再属于这个进程。照这个逻辑来看,应该发生指针错误,无访问权限之类的令系统崩溃的问题才对啊?这个问题牵涉到操作系统的内存管理策略。delete this释放了类对象的内存空间,但是内存空间却并不是马上被回收到系统中,可能是缓冲或者其他什么原因,导致这段内存空间暂时并没有被系统收回。此时这段内存是可以访问的,你可以加上100,加上200,但是其中的值却是不确定的。当你获取数据成员,可能得到的是一串很长的未初始化的随机数;访问虚函数表,指针无效的可能性非常高,造成系统崩溃。&/p&&p&&br&&/p&&p&大致明白在成员函数中调用delete this会发生什么之后,再来看看另一个问题,如果在类的析构函数中调用delete this,会发生什么?实验告诉我们,会导致堆栈溢出。原因很简单,delete的本质是“为将被释放的内存调用一个或多个析构函数,然后,释放内存”(来自effective c++)。显然,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃。&/p&&p&&br&&/p&&p&&b&3.构造函数初始化时必须采用初始化列表一共有三种情况&/b&&/p&&p&1.需要初始化的数据成员是对象(继承时调用基类构造函数)&br&2.需要初始化const修饰的类成员&br&3.需要初始化引用成员数据&/p&&p&&br&&/p&&p&&b&C/C++&/b& &b&内存管理&/b&&/p&&p&&u&&a href=&https://link.zhihu.com/?target=http%3A//blog.csdn.net/bizhu12/article/details/6668834& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&blog.csdn.net/bizhu12/a&/span&&span class=&invisible&&rticle/details/6668834&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/p&&p&&u&&a href=&https://link.zhihu.com/?target=http%3A//blog.csdn.net/wdzxl198/article/details/9050587& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&blog.csdn.net/wdzxl198/&/span&&span class=&invisible&&article/details/9050587&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/p&&p&&br&&/p&&p&&b&4.C/C++&/b& &b&分配内存&/b&&/p&&p&1) malloc函数:void *malloc(unsigned int size)&/p&&p&在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。&/p&&p&2)calloc函数:void *calloc(unsigned int num, unsigned int size)&/p&&p&按照所给的数据个数和数据类型所占字节数,分配一个num * size连续的空间。&/p&&p&calloc申请内存空间后,会自动初始化内存空间为0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。 &br&3)realloc函数:void *realloc(void *ptr, unsigned int size)&/p&&p&动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。&/p&&p&申请的内存空间不会进行初始化。&br&4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在函数体外定义的变量都初始化为0,在函数体内定义的内置类型变量都不进行初始化。&/p&&p&&u&&a href=&https://link.zhihu.com/?target=http%3A//blog.csdn.net/hackbuteer1/article/details/6789164& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&blog.csdn.net/hackbutee&/span&&span class=&invisible&&r1/article/details/6789164&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/p&&p&&br&&/p&&p&&b&处理New分配内存失败情况?&/b&&/p&&p&我们经常会使用new给一个对象分配内存空间,而当内存不够会出现内存不足的情况。C++提供了两种报告方式:&/p&&p&1、抛出bad_alloc异常来报告分配失败;&/p&&p&2、返回空指针,而不会抛出异常&/p&&p&当operator new 无法满足内存需求时,它会不只一次地调用new_handler函数(如果new_handler没有退出程序的话);它会不断地调用,直到找到足够的内存为止。可以用set_new_hander 函数为 new 设置用户自己定义的异常处理函数,也可以让 malloc 享用与 new 相同的异常处理函数。&/p&&p&&br&&/p&&p&&b&当一个类A中没有声命任何成员变量与成员函数这时sizeof(A)的值是多少,如果不是零,请解释一下编译器为什么没有让它为零&/b&&/p&&p&通常是1,用作占位的。&b&为了确保不同对象有不同的地址&/b&&/p&&p&&b&new和malloc的10点区别&/b&&/p&&p&&b&特征&/b&&/p&&p&&b&new/delete&/b&&/p&&p&&b&malloc/free&/b&&/p&&p&分配内存的位置&/p&&p&自由存储区&/p&&p&自由存储区不仅可以是堆,还可以是静态存储区&/p&&p&堆&/p&&p&内存分配成功的返回值&/p&&p&完整类型指针&/p&&p&类型严格与对象匹配,无须进行类型转换,故new是符合&b&类型安全&/b&性的操作符&/p&&p&void*&/p&&p&需要通过强制类型转换将void*指针转换成我们需要的类型&/p&&p&内存分配失败的返回值&/p&&p&默认抛出异常&/p&&p&返回NULL&/p&&p&分配内存的大小&/p&&p&由编译器根据类型计算得出&/p&&p&必须显式指定字节数&/p&&p&处理数组&/p&&p&有处理数组的new版本new[]&/p&&p&需要用户计算数组的大小后进行内存分配&/p&&p&已分配内存的扩充&/p&&p&无法直观地处理&/p&&p&使用realloc简单完成&/p&&p&是否相互调用&/p&&p&可以,看具体的operator new/delete实现&/p&&p&不可调用new&/p&&p&分配内存时内存不足&/p&&p&客户能够指定处理函数或重新制定分配器&/p&&p&无法通过用户代码进行处理&/p&&p&函数重载&/p&&p&允许&/p&&p&不允许&/p&&p&构造函数与析构函数&/p&&p&调用&/p&&p&不调用&/p&&p&&u&&a href=&https://link.zhihu.com/?target=http%3A//www.cnblogs.com/QG-whz/p/5140930.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&cnblogs.com/QG-whz/p/51&/span&&span class=&invisible&&40930.html&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/p&&p&&br&&/p&&p&&b&5.c++如何避免内存泄漏&/b&&/p&&p&a、使用RAII(ResourceAcquisition Is Initialization,资源获取即初始化)技法,以构造函数获取资源(内存),析构函数释放。&/p&&p&b、相比于使用原生指针,更建议使用&b&&a href=&https://link.zhihu.com/?target=http%3A//lib.csdn.net/base/aiplanning& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&智能&/a&&/b&指针,尤其是&b&&a href=&https://link.zhihu.com/?target=http%3A//lib.csdn.net/base/cplusplus& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&C++&/a&&/b&11标准化后的智能指针。&/p&&p&c、注意delete和delete[]的使用方法。&/p&&p&d、这是很复杂的一种情况,是关于类的copy constructor的。首先先介绍一些概念。&/p&&p&同defaultconstructor一样,标准保证,如果类作者没有为class声明一个copy constructor,那么编译器会在需要的时候产生出来(这也是一个常考点:问道&如果类作者未定义出default/copy constructor,编译器会自动产生一个吗?&答案是否定的)&/p&&p&不过请注意!!这里编译器即使产生出来,也是为满足它的需求,而非类作者的需求!!&/p&&p&而什么时候是编译器&需要&的时候呢?是在当这个class【不表现出】bitwise copy semantics(位逐次拷贝,即浅拷贝)的时候。&/p&&p&在4中情况下class【不表现出】bitwisecopy semantics&/p&&p&(1)、当class内含一个memberobject且该member object声明了一个copy
constructor(无论该copy ctor是类作者自己生明的还是编译器合成的);&/p&&p&(2)、当class继承自一个baseclass且该base class有一个copy constructor(无论
该copy ctor是类作者自己生明的还是编译器合成的);&/p&&p&(3)、当class声明了virtual function;&/p&&p&(4)、当class派生自一个继承链,且该链中存在virtual base class时。&/p&&p&言归正传,如果class中仅仅是一些普通资源,那么bitwisecopy semantics是完全够用的;然而,挡在该class中存在了一块动态分配的内存,并且在之后执行了bitwise copy semantics后,将会有一个按位拷贝的对象和原来class中的某个成员指向同一块heap空间,当执行它们的析构函数后,该内存将被释放两次,这是未定义的行为。因此,在必要的时候需要使用Memberwise copy semantics(即深拷贝),来避免内存泄露。&b&位拷贝拷贝的是地址,而值拷贝则拷贝的是内容。&/b&&/p&&p&6.&b&四种情况下编译器会生成默认构造函数&/b&&/p&&p&其实默认构造函数也是分为两类的:有用的、无用的。&/p&&p&所谓&b&有用的标准也是就默认构造函数会为我们的类做一些初始化操作&/b&。那么无用的就不会做任何工作,从而对我们的类也就没有任何意义。所以,我们通常所说的默认构造函数是指&b&有用的默认构造函数,其英文名字叫nontrivial default constructor&/b&。&/p&&p&那么到底什么时候编译器会为我们产生nontrivial default constructor呢?有下面四中情况:&/p&&p&①如果一个类里面某个成员对象有nontrivial default constructor,编译器就会为我们的类产生nontrivial default constructor。&/p&&p&那么编译器这样做的理由是什么?&/p&&p&答案是因为类成员对象有nontrivial default constructor,那么编译器就需要&b&显式的来调用这个类成员对象的&/b&nontrivial default constructor。而编译器想显式的调用类成员对象的nontrivial default constructor,就需要自己来合成一些代码来调用。但是记住,编译器合成的nontrivial default constructor&b&仅仅调用类成员对象的默认构造函数,而不对我们类里面的其它变量做任何初始化操作。&/b&&/p&&p&也就是说,如果你想初始化类成员变量以外的变量例如一个int、一个String,那么必须&b&自己定义&/b&默认构造函数来完成这些变量的初始化。而编译器会对你定义的默认构造函数做相应的扩展,从而调用类成员对象的nontrivial default constructor。&/p&&p&②如果一个派生类的&b&基类有&/b&nontrivial default constructor,那么编译器会为派生类合成一个nontrivial default constructor。&/p&&p&编译器这样的理由是:因为派生类被合成时需要显式调用基类的默认构造函数。&/p&&p&③如何一个类里面隐式的含有&b&任何virtual function table(或vtbl)、pointer member(或vptr)。&/b&&/p&&p&编译器这样做的理由很简单:因为这些vtbl或vptr需要编译器隐式(implicit)的合成出来,那么编译器就把合成动作放到了默认构造函数里面。所以编译器必须自己产生一个默认构造函数来完成这些操作。&/p&&p&所以如果你的类里带有任何&b&virtual function,那么编译器会合成一个默认构造函数。&/b&&/p&&p&④如果一个&b&类虚继承于其它类。&/b&&/p&&p&编译器这样做的理由和③类似:因为虚继承需要维护一个类似指针一样,可以动态的决定内存地址的东西(不同编译器对虚继承的实现不仅相同)。&/p&&p&那么&b&除了以上四种情况,编译器并不会为我们的类产生默认构造函数。&/b&&/p&&p&&br&&/p&&p&&b&7.c++的const函数特点&/b&&/p&&p&1.不能在const函数中修改所在类的对象的数据,因为const函数中的*this是常量,同样只能访问const函数;&/p&&p&2.const函数中只能调用其他的const函数,不能调用非const函数,因为对象调用函数是需要传递对象自己,const函数中的*this是常量,非const函数中的*this是变量,因此不可以调用(除非去除*this的const属性);&/p&&p&Note:使用const_cast后,可以在const函数中调用非const函数的&/p&&p&3.const对象只能调用const函数 ,但是非const对象可以调用const函数;&/p&&p&4.const函数与同名的非const函数是重载函数。&/p&&p&&br&&/p&&p&8.&b&面向对象的三个基本特征&/b&&/p&&p&&b&&u&&a href=&https://link.zhihu.com/?target=http%3A//blog.csdn.net/chenyi8888/article/details/5336912& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&blog.csdn.net/chenyi888&/span&&span class=&invisible&&8/article/details/5336912&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/b&&/p&&p&C++继承体系中,初始化时构造函数的调用顺序如下&/p&&p&(1)任何虚拟基类的构造函数按照他们被继承的顺序构造&/p&&p&(2)任何非虚拟基类的构造函数按照他们被继承的顺序构造&/p&&p&(3)任何成员对象的函数按照他们声明的顺序构造&/p&&p&(4)类自己的构造函数&/p&&p&&br&&/p&&p&9.&b&指针和引用&/b&&/p&&p&1、本质:指针是一个变量,存储内容是一个地址,指向内存的一个存储单元。而引用是原变量的一个别名,实质上和原变量是一个东西,是某块内存的别名。&/p&&p&2、指针的值可以为空,且非const指针可以被重新赋值以指向另一个不同的对象。而引用的值不能为空,并且引用在定义的时候必须初始化,一旦初始化,就和原变量“绑定”,不能更改这个绑定关系。(没有NULL的引用可能会比指针效率更高,因为不用测试有效性)&/p&&p&3、对指针执行sizeof()操作得到的是指针本身的大小(32位系统为4,64位系统为8)。而对引用执行sizeof()操作得到的是所绑定的对象的所占内存大小。&/p&&p&4、指针的自增(++)运算表示对地址的自增,自增大小要看所指向单元的类型。而引用的自增(++)运算表示对值的自增。&/p&&p&5、在作为函数参数进行传递时的区别:指针所以函数传输作为传递时,函数内部的指针形参是指针实参的一个副本,改变指针形参并不能改变指针实参的值,通过解引用*运算符来更改指针所指向的内存单元里的数据。而引用在作为函数参数进行传递时,实质上传递的是实参本身,即传递进来的不是实参的一个拷贝,因此对形参的修改其实是对实参的修改,所以在用引用进行参数传递时,不仅节约时间,而且可以节约空间。&/p&&p&&br&&/p&&p&&b&11.构造函数和析构函数能否重载&/b&&/p&&p&函数重载就是同一函数名的不同实现,并且能在编译时能与一具体形式匹配,这样参数列表必须不一样。由于重载函数与普通函数的差别是没有返回值,而返回值不能确定函数重载,所以构造函数可以重载;析构函数的特点是参数列表为空,并且无返回值,从而不能重载。&/p&&p&&br&&/p&&p&11.&b&析构函数什么情况下定义为虚函数&/b&&/p&&p&一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏。&/p&&p&如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销。当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样会增加类的存储空间。所以只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。&/p&&p&如果基类析构函数不为虚的话,在释放派生类对象的时候就不会调用派生类的析构函数,有可能造成内存泄露。&/p&&p&&br&&/p&&p&12.&b&拷贝构造函数的参数类型必须是引用&/b&&/p&&p&如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数的参数必须是一个引用。&br&需要澄清的是,传指针其实也是传值,如果上面的拷贝构造函数写成CClass(const CClass* c_class),也是不行的。事实上,只有传引用不是传值外,其他所有的传递方式都是传值。&/p&&p&&br&&/p&&p&&b&为什么内联函数,构造函数,静态成员函数不能为virtual函数?&/b&&/p&&p&1& 内联函数&/p&&p&内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾,不能定义内联函数为虚函数。&/p&&p&2& 构造函数&/p&&p&构造函数用来创建一个新的对象,而虚函数的运行是建立在对象的基础上,在构造函数执行时,对象尚未形成,所以不能将构造函数定义为虚函数&/p&&p&3& 静态成员函数&/p&&p&静态成员函数属于一个类而非某一对象,没有this指针,它无法进行对象的判别。&/p&&p&4&
友元函数&/p&&p&C++不支持友元函数的继承,对于没有继承性的函数没有虚函数&/p&&p&这个可以从两个角度去理解:&/p&&p&1。virtual意味着在执行时期进行绑定,所以在编译时刻需确定信息的不能为virtual&/p&&p&构造函数需在编译时刻,因为需构造出个对象,才能执行动作。&br&静态成员函数不属于任何一个对象,编译时刻确定不存在执行的时候选择执行哪个的情形。&br&内联函数,由于属于编译器的建议机制,所以其实可以virtual。&/p&&p&2。virtual意味着派生类可以改写其动作&br&派生类的构造函数会先执行基类的构造函数而不是取代基类构造函数,也就是说基类的构造函数可以看作派生类构造函数的组成,所以并不能改写这个函数。&br&静态成员函数不属于任何一个对象,所以更不能改写其动作了。&/p&&p&inline和virtual不会同时起作用。带virtual的函数在不需要动态绑定调用的时候,就可以inline。&/p&&p&&br&&/p&&p&&b&构造函数和析构函数为什么没有返回值?&/b&&/p&&p&构造函数和析构函数是两个非常特殊的函数:它们没有返回值。这与返回值为void的函数显然不同,后者虽然也不返回任何值,但还可以让它做点别的事情,而构造函数和析构函数则不允许。在程序中创建和消除一个对象的行为非常特殊,就像出生和死亡,而且总是由编译器来调用这些函数以确保它们被执行。如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由客户程序员自己来显式的调用构造函数与析构函数,这样一来,安全性就被人破坏了。另外,析构函数不带任何参数,因为析构不需任何选项。&/p&&p&&br&&/p&&p&&b&C++异常机制&/b&&/p&&p&异常事件在C++中表示为&b&异常对象&/b&。异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统为程序设置当前异常对象,然后执行程序的当前异常处理代码块,在包含了异常出现点的最内层的&b&try&/b&块,依次匹配catch语句中的异常对象(只进行类型匹配,catch参数有时在catch语句中并不会使用到)。若匹配成功,则执行catch块内的异常处理语句,然后接着执行&b&try...catch...&/b&块之后的代码。如果在当前的try...catch...块内找不到&b&匹配&/b&该异常对象的catch语句,则由更外层的try...catch...块来处理该异常;如果当前函数内所有的try...catch...块都不能匹配该异常,则递归回退到调用栈的上一层去处理该异常。如果一直退到主函数main()都不能处理该异常,则调用系统函数terminate()终止程序。&/p&&p&&u&&a href=&https://link.zhihu.com/?target=http%3A//www.cnblogs.com/QG-whz/p/5136883.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&cnblogs.com/QG-whz/p/51&/span&&span class=&invisible&&36883.html&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/p&&p&&br&&/p&&p&&b&析构函数可以抛出异常吗?为什么不能抛出异常?除了资源泄露,还有其他需考虑的因素吗?&/b&&/p&&p&(1)C++中析构函数的执行不应该抛出异常;&/p&&p&(2)如果析构函数抛出异常,则&b&异常点之后的程序不会执行&/b&,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。&/p&&p&(3)通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。析构函数中抛出异常导致程序不明原因的崩溃是许多系统的致命内伤&/p&&p&&b&解决办法:&/b&那就是&b&把异常完全封装在析构函数内部,决不让异常抛出函数之外。如使用&/b&&/p&&p&&b&Try&/b&&/p&&p&&b&{
}&/b&&/p&&p&&b&Catch&/b&&/p&&p&&b&{&/b&/这里可以什么都不做,只是&b&保证catch块的程序抛出的异常不会被扔出析构函数之外&/b&。&b&}&/b&&/p&&p&&br&&/p&&p&&b&&u&&a href=&https://link.zhihu.com/?target=http%3A//www.cnblogs.com/fly1988happy/archive//2442765.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&cnblogs.com/fly1988happ&/span&&span class=&invisible&&y/archive//2442765.html&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/b&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&在构造函数和析构函数中抛出异常会发生什么?什么是栈展开?&/b&&/p&&p&&br&&/p&&p&构造函数中可以抛出异常,&b&构造抛异常之前必须把已经申请的资源释放掉&/b&。&b&这样,就算你的对象是new出来的,也不会造成内存泄漏。&/b&&br&因为析构函数不会被调用,所以抛出异常后,你没机会释放资源。&/p&&p&构造函数中抛出异常时概括性总结&br&(1) C++中通知对象构造失败的唯一方法那就是在构造函数中抛出异常;&/p&&p&(2)
构造函数中抛出异常将导致对象的析构函数不被执行;&/p&&p&(3) 当对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构;&/p&&p&&br&&/p&&p&栈展开:抛出异常时,将暂停当前函数的执行,开始查找匹配的catch子句。首先检查throw本身是否在try块内部,如果是,检查与该try相关的catch子句,看是否可以处理该异常。如果不能处理,就退出当前函数,并且释放当前函数的内存并销毁局部对象,继续到上层的调用函数中查找,直到找到一个可以处理该异常的catch。这个过程称为栈展开(stack unwinding)&/p&&p&&br&&/p&&p&&b&C++保护和私有构造函数与析构函数&/b&&/p&&p&&b&如何定义一个只能在堆上(栈上)生成对象的类?&/b&&/p&&p&构造函数定义为protected后,就意味着你不能在类的外部构造对象了,而只能在外部构造该类的子类的对象&/p&&p&构造函数定义为private后,意味着不仅仅不能在类的外部构造对象了,而且也不能在外部构造该类的子类的对象了,只能通过类的static静态函数来访问类的内部定义的对象,单件singleton模式就是私有构造函数的典型实例&/p&&p&对于堆中的对象,通常都是用new/delete来创建/销毁,当调用new时,它会自动调用相应类的构造函数,当调用delete时,它会自动调用相应类的析构函数。而在栈中产生对象时,对象的创建/销毁是自动完成的,也就是在创建时自动调用构造函数,在销毁时自动调用析构函数,即不需要显示调用new/delete,但有个前提是类的构造/析构函数都必须是public的。&br&析构函数无论是protected还是priavte,其共同作用都是禁止在栈中产生对象,因为无法自动完成析构函数的调用,自然就不能在栈中创建对象了;当然如果在堆上创建对象时,也不能直接delete对象了,因为这样也会在外部析构该对象,但是可以间接完成堆对象的析构&/p&&p&私有和保护析构函数区别在于私有的析构函数不仅禁止了栈中产生对象,而且同时也禁止了继承&/p&&p&来源:牛客网&/p&&p&在C++中,类的对象建立分为两种,一种是静态建立,如A a;另一种是动态建立,如A* ptr=new A;这两种方式是有区别的。&/p&&p&静态建立一个类对象,是由编译器为对象在栈空间中分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。&/p&&p&动态建立类对象,是使用new运算符将对象建立在堆空间中。这个过程分为两步,第一步是执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。&/p&&p&那么如何限制类对象只能在堆或者栈上建立呢?下面分别进行讨论。&/p&&p&&br&&/p&&p&1、只能建立在堆上&/p&&p&类对象只能建立在堆上,就是不能静态建立类对象,即不能直接调用类的构造函数。&/p&&p&容易想到将构造函数设为私有。在构造函数私有之后,无法在类外部调用构造函数来构造类对象,只能使用new运算符来建立对象。然而,前面已经说过,new运算符的执行过程分为两步,C++提供new运算符的重载,其实是只允许重载operator new()函数,而operator()函数用于分配内存,无法提供构造功能。因此,这种方法不可以。&/p&&p&当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期。如果编译器无法调用类的析构函数,情况会是怎样的呢?比如,类的析构函数是私有的,编译器无法调用析构函数来释放内存。所以,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。&/p&&p&因此,将析构函数设为私有,类对象就无法建立在栈上了。代码如下:&/p&&p&1. &b&class&/b& A&/p&&p&2. {&/p&&p&3. &b&public&/b& :&/p&&p&4.
A(){}&/p&&p&5. &b&void&/b& destory(){ &b&delete&/b& &b&this&/b& ;}&/p&&p&6. &b&private&/b& :&/p&&p&7.
~A(){}&/p&&p&8. };&/p&&p&试着使用A来建立对象,编译报错,提示析构函数无法访问。这样就只能使用new操作符来建立对象,构造函数是公有的,可以直接调用。类中必须提供一个destory函数,来进行内存空间的释放。类对象使用完成后,必须调用destory函数。&/p&&p&上述方法的一个缺点就是,无法解决继承问题。如果A作为其它类的基类,则析构函数通常要设为virtual,然后在子类重写,以实现多态。因此析构函数不能设为private。还好C++提供了第三种访问控制,protected。将析构函数设为protected可以有效解决这个问题,类外无法访问protected成员,子类则可以访问。&/p&&p&另一个问题是,类的使用很不方便,使用new建立对象,却使用destory函数释放对象,而不是使用delete。(使用delete会报错,因为delete对象的指针,会调用对象的析构函数,而析构函数类外不可访问)这种使用方式比较怪异。为了统一,可以将构造函数设为protected,然后提供一个public的static函数来完成构造,这样不使用new,而是使用一个函数来构造,使用一个函数来析构。代码如下,类似于单例模式:&/p&&p&1. &b&class&/b& A&/p&&p&2. {&/p&&p&3. &b&protected&/b& :&/p&&p&4.
A(){}&/p&&p&5.
~A(){}&/p&&p&6. &b&public&/b& :&/p&&p&7. &b&static&/b& A* create()&/p&&p&8.
{&/p&&p&9. &b&return&/b& &b&new&/b& A();&/p&&p&10.
}&/p&&p&11. &b&void&/b& destory()&/p&&p&12.
{&/p&&p&13. &b&delete&/b& &b&this&/b& ;&/p&&p&14.
}&/p&&p&15. };&/p&&p&这样,调用create()函数在堆上创建类A对象,调用destory()函数释放内存。&/p&&p&&br&&/p&&p&2、只能建立在栈上&/p&&p&只有使用new运算符,对象才会建立在堆上,因此,只要禁用new}

我要回帖

更多关于 python2.7输出中文 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信