Youmai の Blog


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

Django的F()表达式

发表于 2015-08-10 | 分类于 Django

算是一些基本的高级用法吧,在Django文档的Query-related classes这一章中。主要讲了三个类,F(),Q()和prefetch()。我们下面讲一讲F()。

class F()
F()是代表模型字段的值,也就是说对于一些特殊的字段的操作,我们不需要用python把数据先取到内存中,然后操作,在存储到db中了。

##常见的使用场景

###字段加1(加减乘除运算)

例如我们有个统计点击量的字段,每次更新的操作其实就是把字段的值加1.

一般我们的做法是把这条记录取出来,把相应字段加+1,然后在save,类似下面的代码

# Tintin filed a news story!
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()

当我们使用了F()之后呢? 只需要一行代码

Reporters.objects.filter(name='Tintin').update(stories_filed=F('stories_filed') + 1)

不仅代码量少了,而且这是直接在数据中操作,效率也变高了,特别是并发的情况,减少了多线程同时操作带来的隐患。 但是不支持字符串相加的操作

###字段比较
例如一个合同有两个日期,一个叫做终止日期,一个叫做结束日期,现在要筛选出终止日期小于结束日期的合同。

from django.db.models import F
from contracts.models import Contracts
contracts = Contracts.objects.filter(contract_stop_time__lt=F('end_time'))

如果没有F对象,就没法直接使用rom来查询。

###补充–通过F()避免竞态条件

Another useful benefit of F() is that having the database - rather than Python - update a field’s value avoids a race condition.

If two Python threads execute the code in the first example above, one thread could retrieve, increment, and save a field’s value after the other has retrieved it from the database. The value that the second thread saves will be based on the original value; the work of the first thread will simply be lost.

If the database is responsible for updating the field, the process is more robust: it will only ever update the field based on the value of the field in the database when the save() or update() is executed, rather than based on its value when the instance was retrieved.

python (double star) and (star)

发表于 2015-08-03 | 分类于 python

这个东西我开始根本不知道该怎么搜,找了很久才在stackoverflow上得到了指点,如果你需要搜一些别的答案,可以尝试搜索(double star in python), 下面是一个非常棒的回答。

原文在此

The single star * unpacks the sequence/collection into positional arguments, so you can do this:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

This will unpack the tuple so that it actually executes as:

s = sum(1, 2)

The double star ** does the same, only using a dictionary and thus named arguments:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

You can also combine:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

will execute as:

s = sum(1, 2, c=10, d=15)

Also see section 4.7.4 - Unpacking Argument Lists of the Python documentation.


Additionally you can define functions to take *x and **y arguments, this allows a function to accept any number of positional and/or named arguments that aren’t specifically named in the declaration.

Example:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

or with **:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

this can allow you to specify a large number of optional parameters without having to declare them.

And again, you can combine:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

Django-统计查询

发表于 2015-07-22 | 分类于 Django

##统计
Django 数据库抽象 API 描述了如何创建、检索、更新和删除独立的对象。但是,有时你会需要处理一些有关对象的集合的统计。本文描述如何使用 Django 查询来处理统计。
本文我们将使用以下模型。这些模型用于在线书店图书清单:

class Author(models.Model):
   name = models.CharField(max_length=100)
   age = models.IntegerField()
   friends = models.ManyToManyField('self', blank=True)

class Publisher(models.Model):
   name = models.CharField(max_length=300)
   num_awards = models.IntegerField()

class Book(models.Model):
   isbn = models.CharField(max_length=9)
   name = models.CharField(max_length=300)
   pages = models.IntegerField()
   price = models.DecimalField(max_digits=10, decimal_places=2)
   rating = models.FloatField()
   authors = models.ManyToManyField(Author)
   publisher = models.ForeignKey(Publisher)
   pubdate = models.DateField()

class Store(models.Model):
   name = models.CharField(max_length=300)
   books = models.ManyToManyField(Book)

##生成整个查询集的统计
Django 提供两种方法来产生统计。第一种方法是产生整个 查询集 的统计。假设我们要统计所有书的平均价格。 Djnago 中查询所有书的语句为:

>>> Book.objects.all()

在这个语句后加上一个 aggregate() 子句就行了:

>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

上例中的 all() 是多余的。所以可以简写为:

>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate()子句的参数代表我们要统计的内容,本例中我们要统计 Book 模型中 price 字段的平均值。 查询集参考 中可以找到完整的统计函数列表。
aggregate()是一个 查询集 的未端子句,调用后会返回一个由名称-值配对组成的字典。名称是指统计的名称,值就是统计的值。名称由字段名称加上函数名自动组成。如果你想手动指定统计名称,可以象下例在统计子句中定义:

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

如果你想要生成多个统计,那么只要在统计子句后加上另外的统计子句就可以了。例如,如果要计算所有书价中最高价和最低价,可以这样写:

>>> from django.db.models import Avg, Max, Min, Count
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

##生成查询集中每一个项目的统计
第二种方法是为 查询集 中每个独立的对象生成统计。例如,当你检索一个书单时,可能想知道每本书有几个作者。每本书与每个作者之间是一个多对多的关系,我们要为每本书总结这个关系。

要产生每个对象的统计可以使用 annotate() 子句。当定义一个 annotate() 子句后, 查询集 中的每个对象就可以与特定值关联,相当于每个对象有一个 “注释”。
这种注释的语法与 aggregate() 相同。 annotate() 的每个参数代表一个统计。例如,要计算每本书的作者人数:

# Build an annotated queryset
>>> q = Book.objects.annotate(Count('authors'))
# Interrogate the first object in the queryset
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

和 aggregate() 一样,统计的名称自动由字段名和函数名组成。你也可以在定义统计时指定名称:

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

与 aggregate() 不同的是, annotate() 不是 一个未端子句。 annotate() 子句的输出是一个 查询集 。 这个 查询集 可以和其他查询集一样操作,包括 filter() 、 order_by 或者甚至再调用另一个 annotate() 。

##联合和统计
至此,我们统计的对象都是被查询的模块本身的字段。但是,有时我们要统计的是被查询模块的相关联的模块字段。
在统计函数中定义字段时,可以使用与过滤器中用于指定关联字段的 双下划线符号 。通过这种方法, Django 会自动使用联合来统计相关联的字段。

例如,要统计每个书店中书的价格范围:

>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

上面的例子告诉 Django 检索 Store 模型,联合(通过多对多关系) Book 模型,并且统计 book 模型中的价格字段,计算最大值和最小值。
aggergate() 子句适用同样规则。如果你想知道所有书店中书的最高价和最低价,可以这样:

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))

联合的深度是无限的。例如,要统计所有书的作者的最小年龄,你可以这样:

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

##统计和其他查询子句

###filter() 和 exclude()
在过滤器中也可以使用统计。任何用于一般模型的 filter() (或 exclude() )也可与统计联用。

当与 annotate() 子句联用时,过滤器作用于被统计的对象上。例如要统计书名以 “Django” 开头的书:

>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

当与 aggregate() 子句联用时,过滤器作用于被统计的所有对象上。例如要统计书名以 “Django” 开头的书的平均价格:

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

###过滤统计的值
统计出来的值也可以被过滤。和其他模型一样,统计的结果也可以使用 filter() 和 exclude() 子句来过滤。

例如,要产生一个由两个以上作者的书单可以这样:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

上例先进行统计,然后在统计的结果上使用了过滤器。

###annotate() 和 filter() 子句的顺序
当使用同时包含 annotate() 和 filter() 子句的复杂查询时,要特别小心两种子句的顺序。

当一个 annotate() 子句作用于查询时,该统计只对子句之前的查询起作用。也就是说 filter() 和 annotate() 顺序不同,查询就不同了。查询:

>>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)

和查询:

>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

是不同的。两个查询都会返回至少有一本好书(评分大于 3.0 )的出版商。但是,第一个查询中的统计会提供出版商的所有书的数量;第二个查询中的统计只返回好书的数量。第一个查询中统计先于过滤器,所以过滤器对统计没有作用。而第二个查询过滤器先于统计,所以统计的对象是已经过滤过的。

###order_by()
统计可以作为排序的基础。当你定义一个 order_by 子句时,可以引用 annotate() 子句中的统计。

例如,要依据书的作者人数进行排序,可以这样:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

###values()
通常,统计会针对 查询集 中每一个对象返回一个结果。但是,当使用 values 子句来约束要统计的列时,返回的结果会有所不同。原先统计结果中,统计字段的值相同的项会分组合并统计。
例如,要统计每个作者各自所写的书的平均评分:

>>> Author.objects.annotate(average_rating=Avg('book__rating'))

返回的结果会包含每一个作者及其所写的书的平均计分。

但是,如果使用 values() 子句,返回的结果会有所不同:

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

这个例子中会把作者按名字分组统计,返回的结果中不会有重复的作者名字。名字相同的作者在统计中会作为同一个作者来统计,同名作者所写的书的评分会合并为一个作者的书来统计。

###annotate() 和 values() 子句的顺序
当使用 filter() 子句时, annotate() 和 values() 子句的顺序是非常重要的。如果 values() 子句先于 annotate() 子句,会按照前文所述的方式统计。

但是,如果 annotate() 子句先于 values() 子句,那么统计会作用于整个查询集,而 values() 子句只约束统计输出的字段。

例如,如果我们把前一个例子中的 values() 和 annotate() 子句调换顺序:

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

这个例子会为每一个作者生成唯一的结果。但是在输了的数据中只会包含作者名和 average_rating 的统计。

你可以注意到 average_rating 在例子中显示地定义了。在 annotate() 和 values() 子句的顺序处于这种情况是必须显式定义。

如果 values() 子句先于 annotate() 子句,那么任何统计会自动添加到输出结果中。但是 values() 子句在 annotate() 子句之后,那么必须显式定义统计列。

###缺省排序或 order_by() 子句的副作用
一个查询集中 order_by() 子句中的字段(或一个模型中缺省排序字段)会对输了数据产生影响,即使在 values() 中没有这些字段的定义时也同样会影响。这些特殊的字段会影响统计结果,这种情况在计数统计时尤为明显。

假设有一个这样的模型:

class Item(models.Model):
    name = models.CharField(max_length=10)
    data = models.IntegerField()

    class Meta:
        ordering = ["name"]

这里的重点是作为缺省排序的 name 字段。如果你想要统计不重复的 data 值出现了多少次,你可能会使用如下语句:

# 警告:这个语句不完全正确!
Item.objects.values("data").annotate(Count("id"))

…这个语句看似会根据 data 值分组统计 Item 对象的 id 。但统计结果中 name 字段也会参与其中,所以这个语句实际的是不重复的 (data, name) 配对,而这不是我们所要的结果,因此我们应当这样统计:

Item.objects.values("data").annotate(Count("id")).order_by()

…这里我们通过一个空的“ order_by() ”来清除副作用。

这个行为与在查询集文档中 distinct() 提到的一样。通常的规则是:当你不想要额外的字段在统计结果中产生作用时,必须清空排序的内容或者至少确认 values() 子句中的字段已经限制了这些额外字段。

####Note
你可以会问为什么 Django 不去除这些字段的影响。主要的原因是为维护 distinct() 的一贯性和一个原则: Django 从不 删除你的排序定义(我们不会改变那么模型方法的行为,否则就会违背我们 API stability 策略)。

###对小计进行统计
你可以对小计的结果进行统计。在查询中,你可以使用 aggregate() 子句来对 annotate()的结果进行统计。

例如,假设你要统计每本书的作者人数的平均值,那么首先要计算每本书的作者人数,然后根据这个结果来统计平均值:

>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}

python排序函数

发表于 2015-07-19 | 分类于 python

##sort和sorted
相比于sort,sorted()的使用更为广泛

sorted(iterable, cmp, key, reverse)
s.sort(cmp, key, reverse)

sorted可以作用域任意迭代对象,而sort()一般作用于列表。

reverse = True表示按照逆序排序

##sorted

sorted排序的关键是传入key:

In [35]: students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10), ('michael', 'A', 13), ('mike', 'B', 12), ('jessey', 'C', 15)]

In [36]: students = sorted(students, key=lambda result: result[2], reverse=True)

结果:

In [37]: students
Out[37]:
[('john', 'A', 15),
 ('jessey', 'C', 15),
 ('michael', 'A', 13),
 ('jane', 'B', 12),
 ('mike', 'B', 12),
 ('dave', 'B', 10)]

如果我们对排序有更高的要求,比如在本例中,在按年龄排序之后,我们还希望对年龄相同的同学按照等级进行排序。我们可以这样写key

In [38]: students = sorted(students, key=lambda result: (result[2], result[1]), reverse=True)

In [39]: students
Out[39]:
[('jessey', 'C', 15),
 ('john', 'A', 15),
 ('michael', 'A', 13),
 ('jane', 'B', 12),
 ('mike', 'B', 12),
 ('dave', 'B', 10)]

stackoverflow: Sort a list by multiple attributes?

python中定义常量

发表于 2015-06-13 | 分类于 python

实际开发中我们经常会碰到需要定义常量的情形,c语言提供了const关键字来实现,但是python本身却没有提供const,要想在python代码中定义常量,需要我们自己来实现。

下面定义一个const类。要求符合“命名全部为大写”和“值一旦绑定便不可再修改”这两个条件。代码

class const(object):
    class ConstError(TypeError): 
        pass
    class ConstCaseError(ConstError):
        pass

    def __setattr__(self, name, value):
        if self.__dict__.has_key(name):
            raise self.ConstError, "can't change const.%s" % name
        if not name.isupper():
            raise self.ConstCaseError, "const name '%s' is not all uppercase" % name

        self.__dict__[name] = value

import sys
sys.modules[__name__] = const()

这里通过对常量对应的值进行修改时或者命名不符合规范时抛出异常来满足以上常量的两个条件。

###NOTE

使用sys.modules[name]可以获取一个模块对象,并可以通过该对象获取模块的属性,这儿使用了sys.modules向系统字典注入了一个const对象从而实现了在执行import const时实际获取了一个const实例的功能,sys.module在文档中的描述如下:

sys.modules

This is a dictionary that maps module names to modules which have already been loaded. This can be manipulated to force reloading of modules and other tricks. Note that removing a module from this dictionary is not the same as calling reload() on the corresponding module object.

sys.modules[name] = const()这行代码将系统已经加载的模块列表中的const替换为了const(),即一个const实例

##使用
整个工程需要使用的常量都应该定义在一个文件中。例如你将const.py放在了project.utils目录下,常量被定义在project.apps目录下的project_consts.py文件中,那么应该这么写:

#project_consts.py
from project.utils import const

const.NAME = 'IBM'
const.EMAIL = 'ibm.com'

在其他文件中需要使用这些常量时,用如下方式调用:

from project.apps.project_consts import const

print const.NAME

no module named xxx

发表于 2015-06-09 | 分类于 python

python的常见错误,意思非常明确,这个模块找不到了。导致的原因也很确定:

  1. 确实没有安装这个模块
  2. 路径不对

第一个把模块安装上就完事了,第二个往往是导致这个错误最常见的原因。

如果你能确定你已经安装了这个模块,那肯定是import的路径有问题。import导入时,搜索的路径是有顺序的,查看当前python导入模块的搜索路径,可以在交互命令行中执行下面的命令:

In [1]: import sys

In [2]: sys.path
Out[2]:
['',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/bin',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/PIL',
 '/Library/Python/2.7/site-packages',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/extensions',
 '/Users/youwangqiu/.ipython']

可以看到,我使用的python是现在当前路径(' ')下搜索,然后去系统的各个路径下搜索。而这个机制将导致一个很大的问题。

##不要乱给你的.py文件命名
你写了一个比较大的应用,多个.py文件,相互之间有import。给这些.py文件取名是件麻烦事,一般都是根据文件的功能来取名的嘛。所以你就将一个进行http请求的文件命名成了requests.py(同时,你的应用需要系统的requests模块)。调试整个应用的时候,你发现,这个requests模块怎么和教程上的不一样啊,怎么该有的函数都没有啊,要你有什么卵用!

requests模块说:怪我喽…

大哥,你导入的不是系统的requests模块啊,你导入的是你的requests.py啊,当然什么都没有了。

这里虽然没有报no module named的错误,但是这个错误更难调试,和no module named xxx师出同源,如果你导入的requests模块下的一个函数或类的话,no module named xxx就出现了。

所以说给文件命名之前先考虑清楚,不要潇洒地随便写,不然你就是自己挖坑自己跳。

如果你发现自己确实犯了这个错误,并且改了文件名,请千万记住,还要把当前目录下相应的.pyc删除掉,不然还是没用,至于为什么,请自行参考任何一本python教程。

Django-高效更新数据库

发表于 2015-06-05 | 分类于 Django

模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。

例如说我们现在想要将Apress Publisher的名称由原来的”Apress”更改为”Apress Publishing”。若使用save()方法,如:

>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()

这等同于如下SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';

UPDATE books_publisher SET
    name = 'Apress Publishing',
    address = '2855 Telegraph Ave.',
    city = 'Berkeley',
    state_province = 'CA',
    country = 'U.S.A.',
    website = 'http://www.apress.com'
WHERE id = 52;
(注意在这里我们假设Apress的ID为52)

在这个例子里我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:

>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')

与之等同的SQL语句变得更高效,并且不会引起竞态条件。

UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;

update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值由’U.S.A’更改为’USA’:

>>> Publisher.objects.all().update(country='USA')
2

update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。

丢失私有密钥时连接到虚拟机(2)

发表于 2015-06-05 | 分类于 openstack

原理和丢失私有密钥时连接到虚拟机(1)是一样的,从镜像创建的虚拟机我们也可以将其挂载到别的机器上,只是需要的命令不同。

##具体步骤

  • 关闭你的虚拟机
  • 找到你的虚拟机的id,类似282d0355-a4db-4467-a70b-4783ac2c7ed6这样一串数字
  • 找到你的虚拟机的宿主机,登录到宿主机上
  • 进入到存放虚拟机文件的目录

    cd /var/lib/nova/instances/{id}
    

    这里的id就是你上面虚拟机的id
    这个目录下的内容是这样的

    .
    ├── console.log
    ├── disk
    ├── disk.info
    ├── libvirt.xml
    

    这里的disk文件就是我们要操作的对象

  • 在当前目录下创建sysroot目录

    mkdir sysroot
    
  • 挂载disk文件到本机

    guestmount -a disk --rw -m /dev/sda1  sysroot
    

    Note:如果你是ubuntu的系统,guestmount会报错如下:

    libguestfs: error: /usr/bin/supermin-helper exited with error status 1.
    To see full error messages you may need to enable debugging.
    See http://libguestfs.org/guestfs-faq.1.html#debugging-libguestfs
    

    这个时候你可以执行:

    update-guestfs-appliance
    
  • 进入sysroot并把它当作当前进程的根文件系统

    cd sysroot; chroot .
    
  • 现在就可以修改虚拟机的文件啦!

  • 修改完成后,exit退出chroot的环境

    exit
    cd ..  #回到上一级目录
    umount sysroot
    
  • 启动虚拟机即可

丢失私有密钥时连接到虚拟机(1)

发表于 2015-06-02 | 分类于 openstack

有一些镜像制作的时候,只允许使用ssh登陆。所以一般创建虚拟机时,我们会将自己的公钥通过ec2的meta服务写入虚拟机,而后在本地通过和公钥一起生成的私钥登陆。

但是,既然是钥匙,就具备了“丢“的属性,如果我们丢失了本地的私钥,那不就不能登陆虚拟机了?其实倒也不至于不能登陆,只是费事一点罢了。

原理是这样:你需要将本虚拟机的云硬盘(volume)断开,将云硬盘作为数据卷挂载到另一台虚拟机上,然后修改 authorized_keys 文件,将云硬盘挂载回原虚拟机,重启虚拟机即可。

Note:本教程适用的虚拟机必须是从云硬盘创建的,从镜像或者快照创建的请参考丢失私有密钥时连接到虚拟机(2)

###具体步骤

  • 暂停虚拟机,点击左侧导航栏的云硬盘,选择本机A的云硬盘,将其从本机断开
  • 还是在云硬盘的目录,将此云硬盘挂载到另一台虚拟机B

  • 登陆到虚拟机B,执行lsblk,命令确定volume是否已分区

    ubuntu@key-test1:~$ lsblk
    NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
    vda    253:0    0  10G  0 disk 
    └─vda1 253:1    0  10G  0 part /
    vdb    253:16   0  10G  0 disk 
    └─vdb1 253:17   0  10G  0 part
    

    截图中已经说明,在设备/dev/vdb上连接到key_test1,所以最后一行的vdb1就是我们挂载过来的云硬盘。

  • 创建临时目录以安装volume

ubuntu@key-test1:~$ sudo mkdir /mnt/tempvol
  • 使用之前确定的volume名称或设备名称在临时安装点安装volume(或分区)

    ubuntu@key-test1:~$ sudo mount /dev/vdb1 /mnt/tempvol
    
  • 这样你进入/mnt/tempvol目录就是进入了挂载的云硬盘,修改authorized_keys文件,删除失效的public_key,添加新的public_key

    ubuntu@key-test1:~$ vim /mnt/tempvol/home/ubuntu/.ssh/authorized_keys
    
  • 修改完成后,卸载已连接的volume,以将其重新连接至原始实例

    ubuntu@key-test1:~$ sudo umount /mnt/tempvol
    

Note:卸载时可能会出现设备忙的告警,多试几次即可

回到openstack的控制面,将云硬盘重新挂载到机器A,启动机器A即可从拥有私钥的那台机器上登陆

##参考
Amazon EC2 Key Pairs

Instance Metadata and User Data

[翻译]python中的关键字yield

发表于 2015-06-01 | 分类于 python

这是stackoverflow上的一个问题,这里翻译的是它得票最多的一个答案

#问题
Python关键字yield的作用是什么?用来干什么的?

比如,我正在试图理解下面的代码:

def node._get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

下面是这个函数的调用:

result, candidates = list(), [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

当调用 _get_child_candidates的时候发生了什么?返回了一个列表?返回了一个元素?被重复调用了么? 什么时候这个调用结束呢?

#回答
为了理解什么是 yield,你必须理解什么是生成器。在理解生成器之前,我们需要先理解迭代。

##可迭代对象
当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象:

>>> mylist = [1, 2, 3]
>>> for i in mylist :
...    print(i)
1
2
3

mylist 是一个可迭代的对象。当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代的对象:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist :
...    print(i)
0
1
4

所有你可以应用 for .. in ..语法的叫做一个迭代器:列表,字符串,文件对象……这些可迭代对象易于操作,你可以如你所愿的读取其中的元素。但是你把所有的值都存储到了内存中,如果你有大量数据的话这个方式并不是你想要的。

##生成器
生成器是可以迭代的,但是你只可以读取它一次,因为它并不把所有的值放在内存中,它是实时地生成数据:

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator :
...    print(i)
0
1
4

看起来除了把 [] 换成 () 外没什么不同。但是,你不可以再次运行 for i in mygenerator , 因为生成器只能被迭代一次:先计算出0,然后继续计算1,然后计算4,一个接一个。

##yield关键字
yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器。

>>> def createGenerator() :
...    mylist = range(3)
...    for i in mylist :
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

这个例子没什么用途,但是它让你知道,这个函数会返回一大批你只需要读一次的值.

为了精通 yield ,你必须要理解:当你调用这个函数的时候,函数内部的代码并不立马执行。这个函数只是返回一个生成器对象,这有点蹊跷不是吗。

那么,函数内的代码什么时候执行呢?当你使用for进行迭代的时候.

现在到了关键点了!

第一次迭代中你的函数会执行,从开始到达 yield 关键字,然后返回 yield 后的值作为第一次迭代的返回值. 然后,每次执行这个函数都会继续执行你在函数内部定义的那个循环的下一次,再返回那个值,直到没有可以返回的。

如果生成器内部没有定义 yield 关键字,那么这个生成器被认为成空的。这种情况可能因为是循环进行没了,或者是没有满足 if/else 条件。

##解释你的代码

生成器:

# 在这里定义node对象的方法来返回生成器
def node._get_child_candidates(self, distance, min_dist, max_dist):

  # 每一次你使用生成器对象都会调用这里的代码:

  # 如果node对象仍然有一个左孩子
  # 并且距离合理,返回这个左孩子
  if self._leftchild and distance - max_dist < self._median:
      yield self._leftchild

  # 如果node对象仍然有一个右孩子
  # 并且距离合理,返回这个右孩子
  if self._rightchild and distance + max_dist >= self._median:
      yield self._rightchild

  # 如果函数运行到了这里,生成器就是空的
  # 不可能有除了“左孩子”和“右孩子”之外的值

调用者:

# 建立一个空列表和一个元素是当前对象引用的列表
result, candidates = list(), [self]

# candidates循环 (一开始只拥有一个元素)
while candidates:

    # 获取最后一个candidate并将其从列表中删除
    node = candidates.pop()

    # 获取obj和这个candidate之间的距离        
    distance = node._get_dist(obj)

    # 如果距离ok,追加到result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # 将candidate的孩子添加到candidate列表中
    # 循环会一直进行下去,知道遍历了孩子节点的孩子节点的孩子
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

这个代码包含了几个小部分:

  • 我们对一个列表进行迭代,但是迭代中列表还在不断的扩展。它是一个迭代这些嵌套的数据的简洁方式,即使这样有点危险,因为可能导致无限迭代。 candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 穷尽了生成器的所有值,但 while 不断地在产生新的生成器,它们会产生和上一次不一样的值,既然没有作用到同一个节点上.
  • extend() 是一个迭代器方法,作用于迭代器,并把参数追加到迭代器的后面。

通常我们传给它一个列表参数:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

但是在你的代码中的是一个生成器,这样做很好,因为:

  1. 你不必读两次所有的值
  2. 你可以有很多子对象,但不必叫他们都存储在内存里面。

并且这很奏效,因为Python不关心一个方法的参数是不是个列表。Python只希望它是可以迭代的,所以这个参数可以是列表,元组,字符串,生成器… 这叫做 duck typing,这也是为何Python如此棒的原因之一,但这已经是另外一个故事了…

你可以在这里停下,或者来看看生成器的一些高级用法:

##控制生成器的穷尽

>>> class Bank(): # let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self) :
...        while not self.crisis :
...            yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business
>>> for cash in brand_new_atm :
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

对于控制一些资源的访问来说这很有用。

##Itertools,你最好的朋友
itertools包含了很多特殊的迭代方法。是不是曾想过复制一个迭代器?串联两个迭代器?把嵌套的列表分组?不用创造一个新的列表的 zip/map?

只要 import itertools

需要个例子?让我们看看比赛中4匹马可能到达终点的先后顺序的可能情况:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

##迭代器的内部机理
迭代是一个实现可迭代对象(实现的是 __iter__() 方法)和迭代器(实现的是 __next__()方法)的过程。可迭代对象是你可以从其获取到一个迭代器的任一对象。迭代器是那些允许你迭代可迭代对象的对象。

更多见这个文章 for循环是怎么工作的

1…345…18
You Wangqiu

You Wangqiu

世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也

171 日志
21 分类
24 标签
GitHub 知乎 E-Mail
© 2018 You Wangqiu
由 Hexo 强力驱动
主题 - NexT.Muse