会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 更深入的理解Python中的迭代!

更深入的理解Python中的迭代

时间:2025-11-05 08:46:10 来源:益强数据堂 作者:数据库 阅读:104次

深入探讨 Python 的更深 for 循环来看看它们在底层如何工作,以及为什么它们会按照它们的理解方式工作。

Python 的迭代 for 循环不会像其他语言中的 for 循环那样工作。在这篇文章中,更深我们将深入探讨 Python 的理解 for 循环来看看它们在底层如何工作,以及为什么它们会按照它们的迭代方式工作。 

循环的更深问题

我们将通过看一些“陷阱”开始我们的旅程,在我们了解循环如何在 Python 中工作之后,理解我们将再次看看这些问题并解释发生了什么。迭代 

问题 1:循环两次

假设我们有一个数字列表和一个生成器,更深生成器会返回这些数字的理解平方:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)

我们可以将生成器对象传递给 tuple 构造器,从而使其变为一个元组:

>>> tuple(squares)(1,迭代 4, 9, 25, 49)

如果我们使用相同的生成器对象并将其传给 sum 函数,我们可能会期望得到这些数的更深和,即 88。理解

>>> sum(squares)0

但是迭代我们得到了 0。 

问题 2:包含的检查

让我们使用相同的数字列表和相同的生成器对象:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)

如果我们询问 9 是否在 squares 生成器中,Python 将会告诉我们 9 在 squares 中。但是亿华云如果我们再次询问相同的问题,Python 会告诉我们 9 不在 squares 中。

>>> 9 in squaresTrue>>> 9 in squaresFalse

我们询问相同的问题两次,Python 给了两个不同的答案。 

问题 3 :拆包

这个字典有两个键值对:

>>> counts = {apples: 2, oranges: 1}

让我们使用多个变量来对这个字典进行拆包:

>>> x, y = counts

你可能会期望当我们对这个字典进行拆包时,我们会得到键值对或者得到一个错误。

但是解包字典不会引发错误,也不会返回键值对。当你解包一个字典时,你会得到键:

>>> xapples 

回顾:Python 的 for 循环

在我们了解一些关于这些 Python 片段的逻辑之后,我们将回到这些问题。

Python 没有传统的 for 循环。为了解释我的意思,让我们看一看另一种编程语言的 for 循环。

这是一种传统 C 风格的 for 循环,用 JavaScript 编写:

let numbers = [1, 2, 3, 5, 7];for (let i = 0; i < numbers.length; i += 1) {    print(numbers[i])}

JavaScript、 C、 C++、 Java、 PHP 和一大堆其他编程语言都有这种风格的 for 循环,但是免费源码下载 Python 确实没有

Python 确实没有传统 C 风格的 for 循环。在 Python 中确实有一些我们称之为 for 循环的东西,但是它的工作方式类似于 foreach 循环。

这是 Python 的 for 循环的风格:

numbers = [1, 2, 3, 5, 7]for n in numbers:    print(n)

与传统 C 风格的 for 循环不同,Python 的 for 循环没有索引变量,没有索引变量初始化,边界检查,或者索引递增。Python 的 for 循环完成了对我们的 numbers 列表进行遍历的所有工作。

因此,当我们在 Python 中确实有 for 循环时,我们没有传统 C 风格的 for 循环。我们称之为 for 循环的东西的工作机制与之相比有很大的不同。 

定义:可迭代和序列

既然我们已经解决了 Python 世界中无索引的 for 循环,那么让我们在此之外来看一些定义。WordPress模板

可迭代是任何你可以用 Python 中的 for 循环遍历的东西。可迭代意味着可以遍历,任何可以遍历的东西都是可迭代的。

for item in some_iterable:    print(item)

序列是一种非常常见的可迭代类型,列表,元组和字符串都是序列。

>>> numbers = [1, 2, 3, 5, 7]>>> coordinates = (4, 5, 7)>>> words = "hello there"

序列是可迭代的,它有一些特定的特征集。它们可以从 0 开始索引,以小于序列的长度结束,它们有一个长度并且它们可以被切分。列表,元组,字符串和其他所有序列都是这样工作的。

>>> numbers[0]1>>> coordinates[2]7>>> words[4]o

Python 中很多东西都是可迭代的,但不是所有可迭代的东西都是序列。集合、字典、文件和生成器都是可迭代的,但是它们都不是序列。

>>> my_set = {1, 2, 3}>>> my_dict = {k1: v1, k2: v2}>>> my_file = open(some_file.txt)>>> squares = (n**2 for n in my_set)

因此,任何可以用 for 循环遍历的东西都是可迭代的,序列只是一种可迭代的类型,但是 Python 也有许多其他种类的迭代器。 

Python 的 for 循环不使用索引

你可能认为,Python 的 for 循环在底层使用了索引进行循环。在这里我们使用 while 循环和索引手动遍历:

numbers = [1, 2, 3, 5, 7]i = 0while i < len(numbers):    print(numbers[i])    i += 1

这适用于列表,但它不会对所有东西都起作用。这种循环方式只适用于序列

如果我们尝试用索引去手动遍历一个集合,我们会得到一个错误:

>>> fruits = {lemon, apple, orange, watermelon}>>> i = 0>>> while i < len(fruits):...     print(fruits[i])...     i += 1...Traceback (most recent call last):File "<stdin>", line 2, in <module>TypeError: set object does not support indexing

集合不是序列,所以它们不支持索引。

我们不能使用索引手动对 Python 中的每一个迭代对象进行遍历。对于那些不是序列的迭代器来说,这是行不通的。 

迭代器驱动 for 循环

因此,我们已经看到,Python 的 for 循环在底层不使用索引。相反,Python 的 for 循环使用迭代器

迭代器就是可以驱动可迭代对象的东西。你可以从任何可迭代对象中获得迭代器,你也可以使用迭代器来手动对它的迭代进行遍历。

让我们来看看它是如何工作的。

这里有三个可迭代对象:一个集合,一个元组和一个字符串。

>>> numbers = {1, 2, 3, 5, 7}>>> coordinates = (4, 5, 7)>>> words = "hello there"

我们可以使用 Python 的内置 iter 函数来访问这些迭代器,将一个迭代器传递给 iter 函数总会给我们返回一个迭代器,无论我们正在使用哪种类型的迭代器。

>>> iter(numbers)<set_iterator object at 0x7f2b9271c860>>>> iter(coordinates)<tuple_iterator object at 0x7f2b9271ce80>>>> iter(words)<str_iterator object at 0x7f2b9271c860>

一旦我们有了迭代器,我们可以做的事情就是通过将它传递给内置的 next 函数来获取它的下一项。

>>> numbers = [1, 2, 3]>>> my_iterator = iter(numbers)>>> next(my_iterator)1>>> next(my_iterator)2

迭代器是有状态的,这意味着一旦你从它们中消耗了一项,它就消失了。

如果你从迭代器中请求 next 项,但是其中没有更多的项了,你将得到一个 StopIteration 异常:

>>> next(my_iterator)3>>> next(my_iterator)Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration

所以你可以从每个迭代中获得一个迭代器,迭代器唯一能做的事情就是用 next 函数请求它们的下一项。如果你将它们传递给 next,但它们没有下一项了,那么就会引发 StopIteration 异常。

你可以将迭代器想象成 Pez 分配器(LCTT 译注:Pez 是一个结合玩具的独特复合式糖果),不能重新分配。你可以把 Pez 拿出去,但是一旦 Pez 被移走,它就不能被放回去,一旦分配器空了,它就没用了。 

没有 for 的循环

既然我们已经了解了迭代器和 iter 以及 next 函数,我们将尝试在不使用 for 循环的情况下手动遍历迭代器。

我们将通过尝试将这个 for 循环变为 while 循环:

def funky_for_loop(iterable, action_to_do):    for item in iterable:        action_to_do(item)

为了做到这点,我们需要:

从给定的可迭代对象中获得迭代器 反复从迭代器中获得下一项 如果我们成功获得下一项,就执行 for 循环的主体 如果我们在获得下一项时得到了一个 StopIteration 异常,那么就停止循环 def funky_for_loop(iterable, action_to_do):    iterator = iter(iterable)    done_looping = False    while not done_looping:        try:            item = next(iterator)        except StopIteration:            done_looping = True        else:            action_to_do(item)

我们只是通过使用 while 循环和迭代器重新定义了 for 循环。

上面的代码基本上定义了 Python 在底层循环的工作方式。如果你理解内置的 iter 和 next 函数的遍历循环的工作方式,那么你就会理解 Python 的 for 循环是如何工作的。

事实上,你不仅仅会理解 for 循环在 Python 中是如何工作的,所有形式的遍历一个可迭代对象都是这样工作的。

迭代器协议iterator protocol 是一种很好表示 “在 Python 中遍历迭代器是如何工作的”的方式。它本质上是对 iter 和 next 函数在 Python 中是如何工作的定义。Python 中所有形式的迭代都是由迭代器协议驱动的。

迭代器协议被 for 循环使用(正如我们已经看到的那样):

for n in numbers:    print(n)

多重赋值也使用迭代器协议:

x, y, z = coordinates

星型表达式也是用迭代器协议:

a, b, *rest = numbersprint(*numbers)

许多内置函数依赖于迭代器协议:

unique_numbers = set(numbers)

在 Python 中任何与迭代器一起工作的东西都可能以某种方式使用迭代器协议。每当你在 Python 中遍历一个可迭代对象时,你将依赖于迭代器协议。 

生成器是迭代器

所以你可能会想:迭代器看起来很酷,但它们看起来像一个实现细节,我们作为 Python 的使用者,可能不需要关心它们。

我有消息告诉你:在 Python 中直接使用迭代器是很常见的。

这里的 squares 对象是一个生成器:

>>> numbers = [1, 2, 3]>>> squares = (n**2 for n in numbers)

生成器是迭代器,这意味着你可以在生成器上调用 next 来获得它的下一项:

>>> next(squares)1>>> next(squares)4

但是如果你以前用过生成器,你可能也知道可以循环遍历生成器:

>>> squares = (n**2 for n in numbers)>>> for n in squares:...     print(n)...149

如果你可以在 Python 中循环遍历某些东西,那么它就是可迭代的

所以生成器是迭代器,但是生成器也是可迭代的,这又是怎么回事呢? 

我欺骗了你

所以在我之前解释迭代器如何工作时,我跳过了它们的某些重要的细节。 

生成器是可迭代的

我再说一遍:Python 中的每一个迭代器都是可迭代的,意味着你可以循环遍历迭代器。

因为迭代器也是可迭代的,所以你可以使用内置 next 函数从可迭代对象中获得迭代器:

>>> numbers = [1, 2, 3]>>> iterator1 = iter(numbers)>>> iterator2 = iter(iterator1)

请记住,当我们在可迭代对象上调用 iter 时,它会给我们返回一个迭代器。

当我们在迭代器上调用 iter 时,它会给我们返回它自己:

>>> iterator1 is iterator2True

迭代器是可迭代的,所有的迭代器都是它们自己的迭代器。

def is_iterator(iterable):    return iter(iterable) is iterable

迷惑了吗?

让我们回顾一些这些措辞。

一个可迭代对象是你可以迭代的东西 一个迭代对象器是一种实际上遍历可迭代对象的代理

此外,在 Python 中迭代器也是可迭代的,它们充当它们自己的迭代器。

所以迭代器是可迭代的,但是它们没有一些可迭代对象拥有的各种特性。

迭代器没有长度,它们不能被索引:

>>> numbers = [1, 2, 3, 5, 7]>>> iterator = iter(numbers)>>> len(iterator)TypeError: object of type list_iterator has no len()>>> iterator[0]TypeError: list_iterator object is not subscriptable

从我们作为 Python 程序员的角度来看,你可以使用迭代器来做的唯一有用的事情是将其传递给内置的 next 函数,或者对其进行循环遍历:

>>> next(iterator)1>>> list(iterator)[2, 3, 5, 7]

如果我们第二次循环遍历迭代器,我们将一无所获:

>>> list(iterator)[]

你可以把迭代器看作是惰性迭代器,它们是一次性使用,这意味着它们只能循环遍历一次。

正如你在下面的真值表中所看到的,可迭代对象并不总是迭代器,但是迭代器总是可迭代的:

< 如显示不全,请左右滑动 > 对象 可迭代? 迭代器? 可迭代对象 V ? 迭代器 V V 生成器 V V 列表 V X

全部的迭代器协议

让我们从 Python 的角度来定义迭代器是如何工作的。

可迭代对象可以被传递给 iter 函数,以便为它们获得迭代器。

迭代器:

可以传递给 next 函数,它将给出下一项,如果没有下一项,那么它将会引发 StopIteration 异常 可以传递给 iter 函数,它会返回一个自身的迭代器

这些语句反过来也是正确的:

任何可以在不引发 TypeError 异常的情况下传递给 iter 的东西都是可迭代的 任何可以在不引发 TypeError 异常的情况下传递给 next 的东西都是一个迭代器 当传递给 iter 时,任何返回自身的东西都是一个迭代器

这就是 Python 中的迭代器协议。 

迭代器的惰性

迭代器允许我们一起工作,创建惰性可迭代对象,即在我们要求它们提供下一项之前,它们不做任何事情。因为可以创建惰性迭代器,所以我们可以创建无限长的迭代器。我们可以创建对系统资源比较保守的迭代器,可以节省我们的内存,节省 CPU 时间。 

迭代器无处不在

你已经在 Python 中看到过许多迭代器,我也提到过生成器是迭代器。Python 的许多内置类型也是迭代器。例如,Python 的 enumerate 和 reversed 对象就是迭代器。

>>> letters = [a, b, c]>>> e = enumerate(letters)>>> e<enumerate object at 0x7f112b0e6510>>>> next(e)(0, a)

在 Python 3 中,zip, map 和 filter 也是迭代器。

>>> numbers = [1, 2, 3, 5, 7]>>> letters = [a, b, c]>>> z = zip(numbers, letters)>>> z<zip object at 0x7f112cc6ce48>>>> next(z)(1, a)

Python 中的文件对象也是迭代器。

>>> next(open(hello.txt))hello world\n

在 Python 标准库和第三方库中内置了大量的迭代器。这些迭代器首先惰性迭代器一样,延迟工作直到你请求它们下一项。 

创建你自己的迭代器

知道你已经在使用迭代器是很有用的,但是我希望你也知道,你可以创建自己的迭代器和你自己的惰性迭代器。

下面这个类构造了一个迭代器接受一个可迭代的数字,并在循环结束时提供每个数字的平方。

class square_all:    def __init__(self, numbers):        self.numbers = iter(numbers)    def __next__(self):        return next(self.numbers) * 2    def __iter__(self):        return self

但是在我们开始对该类的实例进行循环遍历之前,没有任何工作要做。

这里,我们有一个无限长的可迭代对象 count,你可以看到 square_all 接受 count 而不用完全循环遍历这个无限长的迭代:

>>> from itertools import count>>> numbers = count(5)>>> squares = square_all(numbers)>>> next(squares)25>>> next(squares)36

这个迭代器类是有效的,但我们通常不会这样做。通常,当我们想要做一个定制的迭代器时,我们会生成一个生成器函数:

def square_all(numbers): for n in numbers: yield n**2

这个生成器函数等价于我们上面所做的类,它的工作原理是一样的。

这种 yield 语句似乎很神奇,但它非常强大:yield 允许我们在调用 next 函数之间暂停生成器函数。yield 语句是将生成器函数与常规函数分离的东西。

另一种实现相同迭代器的方法是使用生成器表达式。

def square_all(numbers): return (n**2 for n in numbers)

这和我们的生成器函数确实是一样的,但是它使用的语法看起来像是一个列表推导一样。如果你需要在代码中使用惰性迭代,请考虑迭代器,并考虑使用生成器函数或生成器表达式。 

迭代器如何改进你的代码

一旦你已经接受了在代码中使用惰性迭代器的想法,你就会发现有很多可能来发现或创建辅助函数,以此来帮助你循环遍历和处理数据。 

惰性求和

这是一个 for 循环,它对 Django queryset 中的所有工作时间求和:

hours_worked = 0for event in events: if event.is_billable(): hours_worked += event.duration

下面是使用生成器表达式进行惰性评估的代码:

billable_times = ( event.duration for event in events if event.is_billable())hours_worked = sum(billable_times)

请注意,我们代码的形状发生了巨大变化。

将我们的计算工作时间变成一个惰性迭代器允许我们能够命名以前未命名(billable_times)的东西。这也允许我们使用 sum 函数,我们以前不能使用 sum 函数是因为我们甚至没有一个可迭代对象传递给它。迭代器允许你从根本上改变你组织代码的方式。 

惰性和打破循环

这段代码打印出日志文件的前 10 行:

for i, line in enumerate(log_file): if i >= 10: break print(line)

这段代码做了同样的事情,但是我们使用的是 itertools.islice 函数来惰性地抓取文件中的前 10 行:

from itertools import islicefirst_ten_lines = islice(log_file, 10)for line in first_ten_lines: print(line)

我们定义的 first_ten_lines 变量是迭代器,同样,使用迭代器允许我们给以前未命名的东西命名(first_ten_lines)。命名事物可以使我们的代码更具描述性,更具可读性。

作为奖励,我们还消除了在循环中使用 break 语句的需要,因为 islice 实用函数为我们处理了中断。

你可以在标准库中的 itertools 中找到更多的迭代辅助函数,以及诸如 boltons 和 more-itertools 之类的第三方库。 

创建自己的迭代辅助函数

你可以在标准库和第三方库中找到用于循环的辅助函数,但你也可以自己创建!

这段代码列出了序列中连续值之间的差值列表。

current = readings[0]for next_item in readings[1:]: differences.append(next_item - current) current = next_item

请注意,这段代码中有一个额外的变量,我们每次循环时都要指定它。还要注意,这段代码只适用于我们可以切片的东西,比如序列。如果 readings 是一个生成器,一个 zip 对象或其他任何类型的迭代器,那么这段代码就会失败。

让我们编写一个辅助函数来修复代码。

这是一个生成器函数,它为给定的迭代中的每个项目提供了当前项和下一项:

def with_next(iterable): """Yield (current, next_item) tuples for each item in iterable.""" iterator = iter(iterable) current = next(iterator) for next_item in iterator: yield current, next_item current = next_item

我们从可迭代对象中手动获取一个迭代器,在它上面调用 next 来获取第一项,然后循环遍历迭代器获取后续所有的项目,跟踪后一个项目。这个函数不仅适用于序列,而且适用于任何类型迭代。

这段代码和以前代码是一样的,但是我们使用的是辅助函数而不是手动跟踪 next_item:

differences = []for current, next_item in with_next(readings): differences.append(next_item - current)

请注意,这段代码不会挂在我们循环周围的 next_item 上,with_next 生成器函数处理跟踪 next_item 的工作。

还要注意,这段代码已足够紧凑,如果我们愿意,我们甚至可以将方法复制到列表推导中来。

differences = [ (next_item - current) for current, next_item in with_next(readings)] 

再次回顾循环问题

现在我们准备回到之前看到的那些奇怪的例子并试着找出到底发生了什么。 

问题 1:耗尽的迭代器

这里我们有一个生成器对象 squares:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)

如果我们把这个生成器传递给 tuple 构造函数,我们将会得到它的一个元组:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)>>> tuple(squares)(1, 4, 9, 25, 49)

如果我们试着计算这个生成器中数字的和,使用 sum,我们就会得到 0:

>>> sum(squares)0

这个生成器现在是空的:我们已经把它耗尽了。如果我们试着再次创建一个元组,我们会得到一个空元组:

>>> tuple(squares)()

生成器是迭代器,迭代器是一次性的。它们就像 Hello Kitty Pez 分配器那样不能重新加载。 

问题 2:部分消耗一个迭代器

再次使用那个生成器对象 squares:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)

如果我们询问 9 是否在 squares 生成器中,我们会得到 True:

>>> 9 in squaresTrue

但是我们再次询问相同的问题,我们会得到 False:

>>> 9 in squaresFalse

当我们询问 9 是否在迭代器中时,Python 必须对这个生成器进行循环遍历来找到 9。如果我们在检查了 9 之后继续循环遍历,我们只会得到最后两个数字,因为我们已经在找到 9 之前消耗了这些数字:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)>>> 9 in squaresTrue>>> list(squares)[25, 49]

询问迭代器中是否包含某些东西将会部分地消耗迭代器。如果没有循环遍历迭代器,那么是没有办法知道某个东西是否在迭代器中。 

问题 3:拆包是迭代

当你在字典上循环时,你会得到键:

>>> counts = {apples: 2, oranges: 1}>>> for key in counts:... print(key)...applesoranges

当你对一个字典进行拆包时,你也会得到键:

>>> x, y = counts>>> x, y(apples, oranges)

循环依赖于迭代器协议,可迭代对象拆包也依赖于有迭代器协议。拆包一个字典与在字典上循环遍历是一样的,两者都使用迭代器协议,所以在这两种情况下都得到相同的结果。 

回顾

序列是迭代器,但是不是所有的迭代器都是序列。当有人说“迭代器”这个词时,你只能假设他们的意思是“你可以迭代的东西”。不要假设迭代器可以被循环遍历两次、询问它们的长度或者索引。

迭代器是 Python 中最基本的可迭代形式。如果你想在代码中做一个惰性迭代,请考虑迭代器,并考虑使用生成器函数或生成器表达式。

最后,请记住,Python 中的每一种迭代都依赖于迭代器协议,因此理解迭代器协议是理解 Python 中的循环的关键。

这里有一些我推荐的相关文章和视频:

Loop Like a Native, Ned Batchelder 在 PyCon 2013 的讲演 Loop Better ,这篇文章是基于这个讲演的 The Iterator Protocol: How For Loops Work,我写的关于迭代器协议的短文 Comprehensible Comprehensions,关于推导和迭代器表达器的讲演 Python: Range is Not an Iterator,我关于范围和迭代器的文章 Looping Like a Pro in Python,DB 的 PyCon 2017 讲演

本文是基于作者去年在 DjangoCon AU、 PyGotham 和 North Bay Python 中发表的 Loop Better 演讲。有关更多内容,请参加将于 2018 年 5 月 9 日至 17 日在 Columbus, Ohio 举办的 PYCON。 

(责任编辑:IT科技)

最新内容
推荐内容
  • 电脑桌面开关布局教程(让你的桌面整洁有序,工作高效利索)
  • Ubuntu是一个流行的Linux操作系统,基于Debian发行版和GNOME桌面环境,和其他Linux发行版相比,Ubuntu非常易用,和Windows相容性很好,非常适合Windows用户的迁移,预装了大量常用软件,中文版的功能也较全,支持拼音输入法,预装了Firefox、Open Office、多媒体播放、图像处理等大多数常用软件,一般会自动安装网卡、音效卡等设备的驱动,对于不打游戏不用网银的用户来说,基本上能用的功能都有了,在Windows操作系统下不用分区即可安装使用,就如同安装一个应用软件那么容易,整个Ubuntu操作系统在Windows下就如同一个大文件一样,很容易卸载掉。下面我就介绍一下Ubuntu操作系统安装使用的方法,供Ubuntu新手参考,希望能起到Linux扫盲的作用。     下载Ubuntu   Ubuntu有三个版本,分别是桌面版(Desktop Edition),服务器版(Server Edition),上网本版(Netbook Remix),普通桌面电脑使用桌面版即可,下载地址请点这里,32位CPU请选择32bit version,上网本则可下载Netbook Remix,目前Ubuntu已经占据三分之一的上网本市场,仅次于Windows XP系统。Google的Chrome操作系统强有力的对手就是Ubuntu Netbook Remix。   目前最新的版本是9.04版,下载后的文件名是ubuntu-9.04-desktop-i386.iso,大小是698M,通过迅雷下载非常快,大约半个小时左右可以下载完毕。   安装Ubuntu   在Windows下可以不用重新分区,直接像安装一个应用程序那样安装Ubuntu,安装方法是,先使用一个虚拟光驱(例如微软的Windows虚拟光驱)装载ubuntu-9.04-desktop-i386.iso文件,然后运行根目录下的wubi.exe,运行前要将本地磁盘的名字都修改为英文名,否则会出现错误信息“UnicodeEncodeError: ascii codec cant encode characters in position 0-3: ordinal not in range(128)”而无法运行。       运行之后,会出现如下界面,选择“Install inside Windows”即可在Windows下直接安装而无需分区。    接着出现下面的安装界面,选择一个磁盘,然后将语言选择为“Chinese(Simplified)简体中文”,Installation size为Ubuntu环境的总共磁盘大小,然后是登录用户名和密码,设置好了以后就点安装继续。    后面的安装操作很简单,不需要手动干预就可以直接安装好整个操作系统,大部分的硬件驱动都可以自动安装好。提示安装完毕后,重启系统,就可以使用Ubuntu了。   自动登录Ubuntu   Ubuntu默认是每次登录都是要输入用户名和密码的,这是基于安全方面的考虑,不过对于桌面版,大家都习惯自己的电脑能自动登录,类似Windows XP系统那样,通过一些设置可以实现Ubuntu自动登录。设置的方法是:点击“系统”—“系统管理”—“登录窗口” (需要输入管理员密码),然后在“安全”选项页—勾选(启用自动登录),然后在下拉列表里选择自己的用户名。之后Ubuntu就能够自动登录了。   开机自动运行程序   类似Windows的启动菜单,在Linux也可以实现开机自动运行一些命令,比较简单的方法是修改 /etc/rc.local 文件,将需要执行的命令添加进去。     桌面设置   Ubuntu的桌面,默认有两个任务栏,一个在上面,一个在下面,通常习惯Windows的用户喜欢将上面的移到下面,Ubuntu的面板无法拖动,在上面点右键后,可以让其显示在屏幕下端。   桌面背景设置和Windows很类似,在“桌面”上点右键,点更改桌面背景,就可以进行修改设置。   修改root密码   Ubuntu默认的用户并不是root,我们可以通过操作来使用root这个超级管理员帐号,以获得更大的权限。先打开终端,然后执行下面的语句   sudo passwd root   就可以修改超级管理员root的密码,之后就可以使用su命令切换到root用户来执行某些更高权限的操作。   Hosts修改   在Windows下,我们上Twitter等网站都需要修改hosts文件,在Linux下也有hosts文件,文件位于/etc/hosts,使用root用户可以编辑修改这个文件,主机名和IP的格式与Windows的完全相同,例如:   127.0.0.1 localhost   在Ubuntu下安装软件   Ubuntu下的软件安装有几种方式,常用的是deb包的安装方式,deb是debian系列的Linux包管理方式,ubuntu属于debian的派生,也默认支持这种软件安装方式,当下载到一个deb格式的软件后,直接在界面上就可以安装。   另一种常见的安装方式是源代码编译安装,很多软件会提供了源代码给最终用户,用户需要自行编译安装,先使用tar将源代码解压缩到一个目录下,然后进入这个目录,执行以下三条命令:   ./configure     make   sudo make install   执行完成后,即可完成软件的编译和安装。   还有一种方式是apt-get的安装方法,APT是Debian及其衍生发行版的软件包管理器,APT可以自动下载,配置,安装二进制或者源代码格式的软件包,因此简化了Unix系统上管理软件的过程。常用的安装命令是:   sudo apt-get install 软件名   sudo apt-get remove 软件名   Firefox浏览器的更新   Ubuntu安装完成后会自动安装一个Firefox浏览器,遗憾的是这个Firefox版本通常较低,例如Ubuntu 9.04会安装Firefox 3.0,不过我们可以想办法下载最新的Firefox覆盖掉老版本Firefox,具体方法是,先上Firefox官方网站下载最新的Linux版本Firefox,然后将其解压缩到某一个目录下,例如firefox目录,进入终端,到这个目录的父目录,执行下面的语句:   sudo cp -r firefox /usr/lib/firefox-3.5.2   sudo mv /usr/bin/firefox /usr/bin/firefox.old   sudo ln -s /usr/lib/firefox-3.5.2/firefox /usr/bin/firefox-3.5.2   sudo ln -s /usr/bin/firefox-3.5.2 /usr/bin/firefox   之后就可以将Firefox成功替换为最新的Firefox 3.52版本,未来的Firefox更新也可以使用这种方法。     Firefox的Flash问题   经过我的实际测试,Ubuntu自动安装的Flash插件swfdec存在很多问题,在Firefox中,很多网页的Flash无法显示,包括Google音乐和开心网等,因此建议使用下面两条语句将其卸载。   sudo apt-get remove swfdec-mozilla   sudo apt-get remove swfdec-gnome   之后可安装官方的Adobe Flash Player的Linux版,下载地址是: http://get.adobe.com/flashplayer/   安装完成后,还要解决中文乱码问题,解决方法是执行下面语句:   sudo cp /etc/fonts/conf.d/49-sansserif.conf /etc/fonts/conf.d/49-sansserif.conf.bak   sudo rm /etc/fonts/conf.d/49-sansserif.conf   之后,Firefox的Flash就完全正常了,在Firefox中访问开心网等Flash网站,显示都正常。   安装常用软件   介绍完了安装的方法和Firefox,下面就可以去各个网站下载一些常用的Linux软件来安装了,下面是我整理的一些常用的Linux软件列表:   Linux QQ:访问这个地址,下载deb文件安装,可以在Linux下玩腾讯QQ。   防火墙 firestarter: 使用 sudo apt-get install firestarter 安装。   杀毒软件 AntiVir: 虽然Linux下的病毒很少,但对于新手还是有必要安装一个杀毒软件,访问这个地址可以下载免费版的AntiVir杀毒软件,这个软件我曾经在《五个最佳的防病毒软件》中介绍过。   rpm 转 deb 工具: 使用 sudo apt-get install alien 安装   JAVA环境安装: JRE的安装 sudo apt-get install sun-java6-jre ,JDK的安装 sudo apt-get install sun-java6-jdk       eclipse安装: 先到这个地址下载最新的eclipse,然后使用tar xvfz eclipse-php-galileo-linux-gtk.tar.gz -C /opt 解压缩后就可以使用。    Picasa 3 for Linux安装: 访问这个地址,下载后直接安装。   Google Earth安装: 在这里下载最新版本的Google Earth,下载下来是个BIN文件,在图形界面上右击 GoogleEarthLinux.bin,在“权限”选项卡中勾选“允许以程序执行文件”,如下图。    之后在终端上执行 ./GoogleEarthLinux.bin 即可安装。    安装LAMP环境   Ubuntu的桌面版也可以安装LAMP(Linux + Apache + MySQL + PHP)环境,这里我介绍一个最简单的方法,就是使用XAMPP,这个项目我曾经在《常见的WAMP集成环境》中介绍过,XAMPP不但支持Windows,还支持Linux,在其网站下载之后,运行下面两条命令:   tar xvfz xampp-linux-1.7.2.tar.gz -C /opt   /opt/lampp/lampp start   就可以启动LAMP环境,XAMPP是功能全面的集成环境,软件包中包含Apache、MySQL、SQLite、PHP、Perl、FileZilla FTP Server、Tomcat等等,很适合开发环境使用。   安装程序添加程序菜单和桌面       有些程序是直接解压缩安装的,因此不会添加“应用程序”的菜单项,我们可以手动将其添加菜单项,具体方法是,打开“系统”—“首选项”—“主菜单”,新增即可。   添加桌面快捷方式是,在桌面上点右键,创建启动器。这个“启动器”就是Windows里面的“快捷方式”。   将“应用程序”的菜单项创建到桌面快捷方式的方法是,在“应用程序”的菜单项上单击鼠标右键,选择“将此启动器添加到桌面”或“将此启动器添加到面板”,就可以了。
  • 什么是Kdump?Kdump 是一种的新的crash dump捕获机制,用来捕获kernel crash时候产生的crash dump。Kdump需要配置两个不同目的的kernel,其中一个我们在这里称作standard(production) kernel;另外一个称之为Crash(capture)kernel。standard(production)kernel,是指我正在使用的kernel,当standard kernel在使用的过程中出现crash的时候, kdump会切换到crash kernel, 简单来说,standard kernel会正运行时发生crash,而crash(capture) Kernel 会被用来捕获production kernel crash时候产生的crash dump。捕获crash dump是在新的crash(capture) kernel 的上下文中来捕获的,而不是在standard kernel上下文进行。具体是当standard kernel方式crash的时候,kdump通过kexec(后面介绍)自动启动进入到crash kernel当中。假如启动了kdump服务,standard kernel会预留一部分内存, 这部分内存用来启动crash kernel。kdump机制主要包括两个组件:kdump和kexec kexec是一个快速启动机制,允许通过已经运行的内核的上下文启动一个Linux内核,不需要经过BIOS。BIOS可能会消耗很多时间,特别是带有众多数量的外设的大型服务器。这种办法可以为经常启动机器的开发者节省很多时间。Kexec是实现kdump机制的关键,它包括2个组成部分:一是内核空间的系统调用kexec_load,负责在生产内核(production kernel 或 first kernel)启动时将捕获内核(capture kernel或sencond kernel)加载到指定地址。二是用户空间的工具kexec-tools,他将捕获内核的地址传递给生产内核,从而在系统崩溃的时候能够找到捕获内核的地址并运行。没有kexec就没有kdump。先有kexec实现了在一个内核中可以启动另一个内核,才让kdump有了用武之地。 kdump是一种先进的基于kexec的内核崩溃转储机制。当系统崩溃时,kdump使用kexec 启动到第二个内核。第二个内核通常叫做捕获内核,以很小内存启动以捕获转储镜像。第一个内核保留了内存的一部分给第二内核启动用。由于kdump利用kexec启动捕获内核,绕过了 BIOS,所以第一个内核的内存得以保留。这是内核崩溃转储的本质。kdump需要两个不同目的的内核,生产内核和捕获内核。生产内核是捕获内核服务的对像。捕获内核会在生产内核崩溃时启动起来,与相应的ramdisk一起组建一个微环境,用以对生产内核下的内存进行收集和转存。注意,在启动时,kdump保留了一定数量的重要的内存,为了计算系统需要的真正最小内存,加上kdump使用的内存数量,以决定真正的最小内存的需求。kexec和kdump的设计区别:Kexec的设计是用新内核去覆盖原内核位置;而KDUMP是预留一块内存来加载第二个内核(和相关数据),Crash后第二个内核在原位置运行(不然就达不到相关目的了),收集第一个内核的相关内存信息。下面开始试验kdump特性: 操作系统:ubuntu 12.10(3.5.0-17-generic)安装kdump工具复制代码代码如下: 发现安装过程中修改了grub,在引导内核配置上(/boot/grub/grub.cfg)多了如下参数复制代码代码如下: crashkernel用来指定保留内存的大小,我们可以知道crashkernel帮我们设定的保留区域的大小是:假如内存小于384M,不保留内存;假如内存大于等于384M但小于2G,保留64M;假如内存大于2G,保留128M。 复制代码代码如下:下载dbgsym文件,改文件是用来吊事内核信息的文件复制代码代码如下: 重启机器使配置生效。复制代码代码如下:kdump-tools配置(kdump-config show):复制代码代码如下:可以通过sysrq强制系统崩溃。 复制代码代码如下: 这造成内核崩溃,如配置有效,系统将重启进入kdump内核,当系统进程进入到启动 kdump服务的点时,(dump.时间戳文件)将会拷贝到你在kdump配置文件中设置的位置。ubuntu的缺省目录是:/var/crash/时间戳文件夹。然后系统重启进入到正常的内核。一旦回复到正常的内核,就可以在上述的目录下发现dump文件,即内存转储文件。可以使用之前安装的crash工具来进行分析。生成dump文件后/var/crash的目录结构:├── 201305061817复制代码代码如下:出现如下错误提示: crash: cannot resolve: xtime,此时crash的版本为5.1.6,版本太低,调试不了3.5的内核,需要升级crash,可以手动安装crash。
  • ubuntu怎么开启root帐号?ubuntu 的root账户具有最高的系统权限,它类似于windows系统中的管理员账号,但是比windows系统中管理员账号的权限更高,一般都情况下不要使用root账户,但是有的时候还是要使用root账户,下面就为大家介绍ubuntu 开启root帐号方法!说明:小编的这个建议只适合于10.10之前版本的ubuntu系统,后面的11.04,11.10。。。14.04系统因为采用的默认桌面不同,所以本经验不适用,若要使用,那么请安装GNOME桌面ubuntu 开启root帐号方法:1、点击系统菜单栏中的“应用程序”,然后点击附件,之后在打开的附件子菜单中选择“终端”2、点击终端后就打开如下图所示的终端工具,大家要做的所有操作都在这个窗口中进行3、其实root账户是存在的,只是需要我们给它设置一个密码,然后使用的时候用root用户名登陆,然后输入对应的密码就就以root用户登录了,所以开启root账户,实际上就是给root用户设置一个密码的过程,下面我们就来给root设置密码,另外还需要注意的是,只能使安全ubuntu系统的时候创建的用户账号才能启用root账号,使用下面的命令来给root账号设置密码:sudo passwd root执行上面的命令后,就会提示要求输入当前用户的密码4、确认我们的密码正确后,就会提示“Enter new UNIX password”,这个就是设置密码的提示,输入大家要设置的密码,注意在设置密码的时候是看不到任何字符的,只有我们自己注意了5、输入密码后敲回车键,之后会提示我们再次确认密码,输入确认密码,然后按Enter回车键6、密码设置成功,这样root用户也就开启了,以后大家要使用root账号的时候只要使用相关切换命令就可以了,具体怎么切换到root账号的命令请看小编另外一个经验的分享END以上就是ubuntu怎么开启root帐号的方法,希望对大家有所帮助!
  • 解决电脑启动风扇错误的方法(常见电脑风扇错误及其解决办法)
  • 今天开始调试arm的板子,要通过tftp下载到板子上,所以又要配置tftp服务器,真的烦死了… (本人酷爱装系统,所以经常都要搞配置) 因为之前已经在Ubuntu下搭建过很多次tftp服务器了,但是一直记不住那一个配置文件的名字(记性太差),所以每次都要百度。 可是每次百度出来的结果都不一样,而且出入很大,有的过程相当的繁琐,不仅很麻烦,而且搞到最后还没有配置成功。所以干脆写个日志记下来。 tftp服务器最简单安装配置1.安装tftp-server sudo apt-get install tftpd-hpa sudo apt-get install tftp-hpa(假如不需要客户端可以不安装) tftp-hpa是客户端 tftpd-hpa是服务器端 2.配置TFTP服务器 sudo vim /etc/default/tftpd-hpa 将原来的内容改为: TFTP_USERNAME=”tftp” TFTP_ADDRESS=”0.0.0.0:69″ TFTP_DIRECTORY=”tftp根目录” #服务器目录,需要设置权限为777,chomd 777 TFTP_OPTIONS=”-l -c -s” 3.重新启动TFTP服务 sudo service tftpd-hpa restart 4.测试
热点内容