如何在Django模板的词典中遍历词典?

问题:如何在Django模板的词典中遍历词典?

我的字典看起来像这样(字典中的字典):

{'0': {
    'chosen_unit': <Unit: Kg>,
    'cost': Decimal('10.0000'),
    'unit__name_abbrev': u'G',
    'supplier__supplier': u"Steve's Meat Locker",
    'price': Decimal('5.00'),
    'supplier__address': u'No\r\naddress here',
    'chosen_unit_amount': u'2',
    'city__name': u'Joburg, Central',
    'supplier__phone_number': u'02299944444',
    'supplier__website': None,
    'supplier__price_list': u'',
    'supplier__email': u'ss.sss@ssssss.com',
    'unit__name': u'Gram',
    'name': u'Rump Bone',
}}

现在,我只是想在模板上显示信息,但是我很挣扎。我的模板代码如下:

{% if landing_dict.ingredients %}
  <hr>
  {% for ingredient in landing_dict.ingredients %}
    {{ ingredient }}
  {% endfor %}
  <a href="/">Print {{ landing_dict.recipe_name }}</a>
{% else %}
  Please search for an ingredient below
{% endif %}

它只是在模板上显示“ 0”?

我也尝试过:

{% for ingredient in landing_dict.ingredients %}
  {{ ingredient.cost }}
{% endfor %}

这甚至不显示结果。

我认为也许我需要更深一层地迭代,所以尝试了一下:

{% if landing_dict.ingredients %}
  <hr>
  {% for ingredient in landing_dict.ingredients %}
    {% for field in ingredient %}
      {{ field }}
    {% endfor %}
  {% endfor %}
  <a href="/">Print {{ landing_dict.recipe_name }}</a>
{% else %}
  Please search for an ingredient below
{% endif %}

但这不会显示任何内容。

我究竟做错了什么?

My dictionary looks like this(Dictionary within a dictionary):

{'0': {
    'chosen_unit': <Unit: Kg>,
    'cost': Decimal('10.0000'),
    'unit__name_abbrev': u'G',
    'supplier__supplier': u"Steve's Meat Locker",
    'price': Decimal('5.00'),
    'supplier__address': u'No\r\naddress here',
    'chosen_unit_amount': u'2',
    'city__name': u'Joburg, Central',
    'supplier__phone_number': u'02299944444',
    'supplier__website': None,
    'supplier__price_list': u'',
    'supplier__email': u'ss.sss@ssssss.com',
    'unit__name': u'Gram',
    'name': u'Rump Bone',
}}

Now I’m just trying to display the information on my template but I’m struggling. My code for the template looks like:

{% if landing_dict.ingredients %}
  <hr>
  {% for ingredient in landing_dict.ingredients %}
    {{ ingredient }}
  {% endfor %}
  <a href="/">Print {{ landing_dict.recipe_name }}</a>
{% else %}
  Please search for an ingredient below
{% endif %}

It just shows me ‘0’ on my template?

I also tried:

{% for ingredient in landing_dict.ingredients %}
  {{ ingredient.cost }}
{% endfor %}

This doesn’t even display a result.

I thought perhaps I need to iterate one level deeper so tried this:

{% if landing_dict.ingredients %}
  <hr>
  {% for ingredient in landing_dict.ingredients %}
    {% for field in ingredient %}
      {{ field }}
    {% endfor %}
  {% endfor %}
  <a href="/">Print {{ landing_dict.recipe_name }}</a>
{% else %}
  Please search for an ingredient below
{% endif %}

But this doesn’t display anything.

What am I doing wrong?


回答 0

可以说您的数据是-

data = {'a': [ [1, 2] ], 'b': [ [3, 4] ],'c':[ [5,6]] }

您可以使用该data.items()方法来获取字典元素。请注意,在Django模板中,我们不放置()。另外提到的一些用户values[0]不起作用,如果是这种情况,请尝试values.items

<table>
    <tr>
        <td>a</td>
        <td>b</td>
        <td>c</td>
    </tr>

    {% for key, values in data.items %}
    <tr>
        <td>{{key}}</td>
        {% for v in values[0] %}
        <td>{{v}}</td>
        {% endfor %}
    </tr>
    {% endfor %}
</table>

相当确定您可以将此逻辑扩展到您的特定字典。


要以排序的顺序遍历dict键 -首先,我们在python中排序,然后在django模板中进行迭代和渲染。

return render_to_response('some_page.html', {'data': sorted(data.items())})

在模板文件中:

{% for key, value in data %}
    <tr>
        <td> Key: {{ key }} </td> 
        <td> Value: {{ value }} </td>
    </tr>
{% endfor %}

Lets say your data is –

data = {'a': [ [1, 2] ], 'b': [ [3, 4] ],'c':[ [5,6]] }

You can use the data.items() method to get the dictionary elements. Note, in django templates we do NOT put (). Also some users mentioned values[0] does not work, if that is the case then try values.items.

<table>
    <tr>
        <td>a</td>
        <td>b</td>
        <td>c</td>
    </tr>

    {% for key, values in data.items %}
    <tr>
        <td>{{key}}</td>
        {% for v in values[0] %}
        <td>{{v}}</td>
        {% endfor %}
    </tr>
    {% endfor %}
</table>

Am pretty sure you can extend this logic to your specific dict.


To iterate over dict keys in a sorted order – First we sort in python then iterate & render in django template.

return render_to_response('some_page.html', {'data': sorted(data.items())})

In template file:

{% for key, value in data %}
    <tr>
        <td> Key: {{ key }} </td> 
        <td> Value: {{ value }} </td>
    </tr>
{% endfor %}

回答 1

这个答案对我没有用,但是我自己找到了答案。但是,没有人发布我的问题。我懒得先问再回答,所以就把它放在这里。

这用于以下查询:

data = Leaderboard.objects.filter(id=custom_user.id).values(
    'value1',
    'value2',
    'value3')

在模板中:

{% for dictionary in data %}
  {% for key, value in dictionary.items %}
    <p>{{ key }} : {{ value }}</p>
  {% endfor %}
{% endfor %}

This answer didn’t work for me, but I found the answer myself. No one, however, has posted my question. I’m too lazy to ask it and then answer it, so will just put it here.

This is for the following query:

data = Leaderboard.objects.filter(id=custom_user.id).values(
    'value1',
    'value2',
    'value3')

In template:

{% for dictionary in data %}
  {% for key, value in dictionary.items %}
    <p>{{ key }} : {{ value }}</p>
  {% endfor %}
{% endfor %}

回答 2

如果将变量data(字典类型)作为上下文传递给模板,则代码应为:

{% for key, value in data.items %}
    <p>{{ key }} : {{ value }}</p> 
{% endfor %}

If you pass a variable data (dictionary type) as context to a template, then you code should be:

{% for key, value in data.items %}
    <p>{{ key }} : {{ value }}</p> 
{% endfor %}

python中的类常量

问题:python中的类常量

在python中,我希望一个类具有一些在所有子类中通用的“常量”(实际上是变量)。有没有办法用友好的语法做到这一点?现在我使用:

class Animal:
    SIZES=["Huge","Big","Medium","Small"]

class Horse(Animal):
    def printSize(self):
        print(Animal.SIZES[1])

我想知道是否有更好的方法或者不用写“动物”就能做到。之前的大小。谢谢!编辑:忘记提及马从动物继承。

In python, I want a class to have some “constants” (practically, variables) which will be common in all subclasses. Is there a way to do it with friendly syntax? Right now I use:

class Animal:
    SIZES=["Huge","Big","Medium","Small"]

class Horse(Animal):
    def printSize(self):
        print(Animal.SIZES[1])

and I’m wondering if there is a better way to do it or a way to do it without then having to write “Animal.” before the sizes. Thanks! edit: forgot to mention that horse inherits from animal.


回答 0

由于Horse是的子类Animal,您可以更改

print(Animal.SIZES[1])

print(self.SIZES[1])

不过,您仍然需要记住这SIZES[1]意味着“大”,因此您可以通过执行以下操作来改进代码:

class Animal:
    SIZE_HUGE="Huge"
    SIZE_BIG="Big"
    SIZE_MEDIUM="Medium"
    SIZE_SMALL="Small"

class Horse(Animal):
    def printSize(self):
        print(self.SIZE_BIG)

或者,您可以创建中间类:HugeAnimalBigAnimal等等。如果每个动物类别都包含不同的逻辑,那将特别有用。

Since Horse is a subclass of Animal, you can just change

print(Animal.SIZES[1])

with

print(self.SIZES[1])

Still, you need to remember that SIZES[1] means “big”, so probably you could improve your code by doing something like:

class Animal:
    SIZE_HUGE="Huge"
    SIZE_BIG="Big"
    SIZE_MEDIUM="Medium"
    SIZE_SMALL="Small"

class Horse(Animal):
    def printSize(self):
        print(self.SIZE_BIG)

Alternatively, you could create intermediate classes: HugeAnimal, BigAnimal, and so on. That would be especially helpful if each animal class will contain different logic.


回答 1

您可以SIZES通过self.SIZES(在实例方法中)或cls.SIZES(在类方法中)到达。

无论如何,您都必须明确说明在哪里可以找到SIZES。一种替代方法是将SIZES包含类的模块放入,但随后您需要在单个模块中定义所有类。

You can get to SIZES by means of self.SIZES (in an instance method) or cls.SIZES (in a class method).

In any case, you will have to be explicit about where to find SIZES. An alternative is to put SIZES in the module containing the classes, but then you need to define all classes in a single module.


回答 2

class Animal:
    HUGE = "Huge"
    BIG = "Big"

class Horse:
    def printSize(self):
        print(Animal.HUGE)
class Animal:
    HUGE = "Huge"
    BIG = "Big"

class Horse:
    def printSize(self):
        print(Animal.HUGE)

回答 3

扩展betabandido的答案,您可以编写一个函数将属性作为常量注入模块:

def module_register_class_constants(klass, attr_prefix):
    globals().update(
        (name, getattr(klass, name)) for name in dir(klass) if name.startswith(attr_prefix)
    )

class Animal(object):
    SIZE_HUGE = "Huge"
    SIZE_BIG = "Big"

module_register_class_constants(Animal, "SIZE_")

class Horse(Animal):
    def printSize(self):
        print SIZE_BIG

Expanding on betabandido’s answer, you could write a function to inject the attributes as constants into the module:

def module_register_class_constants(klass, attr_prefix):
    globals().update(
        (name, getattr(klass, name)) for name in dir(klass) if name.startswith(attr_prefix)
    )

class Animal(object):
    SIZE_HUGE = "Huge"
    SIZE_BIG = "Big"

module_register_class_constants(Animal, "SIZE_")

class Horse(Animal):
    def printSize(self):
        print SIZE_BIG

python是否有排序列表?

问题:python是否有排序列表?

我所说的结构是:

  • x.push()操作复杂度O(log n)
  • O(log n)查找元素的复杂度
  • O(n)复杂度进行计算list(x)将被排序

我也有一个有关性能的相关问题list(...).insert(...),现在在这里

By which I mean a structure with:

  • O(log n) complexity for x.push() operations
  • O(log n) complexity to find an element
  • O(n) complexity to compute list(x) which will be sorted

I also had a related question about performance of list(...).insert(...) which is now here.


回答 0

标准Python列表不以任何形式排序。标准的heapq模块可用于将O(log n)追加到现有列表中,并删除O(log n)中最小的模块,但在定义中不是排序列表。

有许多符合您需求的Python平衡树实现,例如rbtreeRBTreepyavl

The standard Python list is not sorted in any form. The standard heapq module can be used to append in O(log n) to an existing list and remove the smallest one in O(log n), but isn’t a sorted list in your definition.

There are various implementations of balanced trees for Python that meet your requirements, e.g. rbtree, RBTree, or pyavl.


回答 1

您的big-O需求是否有特定原因?还是您只是想要它快?所述sortedcontainers模块是纯Python和快速(如在快速作为-C实现比如blist和rbtree)。

性能比较表明它与基准测试blist的排序列表类型更快或看齐。还要注意,rbtree,RBTree和PyAVL提供排序的dict和set类型,但没有排序的列表类型。

如果需要性能,请始终记住要进行基准测试。在它还显示基准比较之前,应该怀疑使用Big-O表示法证明快速的模块。

免责声明:我是Python sortedcontainers模块的作者。


安装:

pip install sortedcontainers

用法:

>>> from sortedcontainers import SortedList
>>> l = SortedList()
>>> l.update([0, 4, 1, 3, 2])
>>> l.index(3)
3
>>> l.add(5)
>>> l[-1]
5

Is there a particular reason for your big-O requirements? Or do you just want it to be fast? The sortedcontainers module is pure-Python and fast (as in fast-as-C implementations like blist and rbtree).

The performance comparison shows it benchmarks faster or on par with blist’s sorted list type. Note also that rbtree, RBTree, and PyAVL provide sorted dict and set types but don’t have a sorted list type.

If performance is a requirement, always remember to benchmark. A module that substantiates the claim of being fast with Big-O notation should be suspect until it also shows benchmark comparisons.

Disclaimer: I am the author of the Python sortedcontainers module.


Installation:

pip install sortedcontainers

Usage:

>>> from sortedcontainers import SortedList
>>> l = SortedList()
>>> l.update([0, 4, 1, 3, 2])
>>> l.index(3)
3
>>> l.add(5)
>>> l[-1]
5

回答 2

尽管我仍然从未检查过基本Python列表操作的“大O”速度,但bisect在这种情况下,标准模块可能也值得一提:

import bisect
L = [0, 100]

bisect.insort(L, 50)
bisect.insort(L, 20)
bisect.insort(L, 21)

print L
## [0, 20, 21, 50, 100]

i = bisect.bisect(L, 20)
print L[i-1], L[i]
## 20, 21

PS。啊,对不起,bisect在提到的问题中被提及。不过,我认为,如果此信息在这里,不会有太大危害)

PPS。而CPython的名单实际上是数组(不是,比方说,skiplists或等)。好吧,我想它们一定很简单,但就我而言,这个名称有点误导。


因此,如果我没记错的话,平分/列表速度可能是:

  • 对于push():在最坏的情况下为O(n);
  • 搜索:如果我们认为数组索引的速度为O(1),则搜索应为O(log(n))操作;
  • 用于创建列表:O(n)应该是列表复制的速度,否则为同一列表的O(1))

更新。在评论中进行讨论之后,让我在这里链接这些SO问题:如何实现Python的列表以及什么是Python列表函数的运行时复杂性

Though I have still never checked the “big O” speeds of basic Python list operations, the bisect standard module is probably also worth mentioning in this context:

import bisect
L = [0, 100]

bisect.insort(L, 50)
bisect.insort(L, 20)
bisect.insort(L, 21)

print L
## [0, 20, 21, 50, 100]

i = bisect.bisect(L, 20)
print L[i-1], L[i]
## 20, 21

PS. Ah, sorry, bisect is mentioned in the referenced question. Still, I think it won’t be much harm if this information will be here )

PPS. And CPython lists are actually arrays (not, say, skiplists or etc) . Well, I guess they have to be something simple, but as for me, the name is a little bit misleading.


So, if I am not mistaken, the bisect/list speeds would probably be:

  • for a push(): O(n) for the worst case ;
  • for a search: if we consider the speed of array indexing to be O(1), search should be an O(log(n)) operation ;
  • for the list creation: O(n) should be the speed of the list copying, otherwise it’s O(1) for the same list )

Upd. Following a discussion in the comments, let me link here these SO questions: How is Python’s List Implemented and What is the runtime complexity of python list functions


回答 3

import bisect

class sortedlist(list):
    '''just a list but with an insort (insert into sorted position)'''
    def insort(self, x):
        bisect.insort(self, x)
import bisect

class sortedlist(list):
    '''just a list but with an insort (insert into sorted position)'''
    def insort(self, x):
        bisect.insort(self, x)

回答 4

尽管(尚未)提供自定义搜索功能,但该heapq模块可能适合您的需求。它使用常规列表实现堆队列。您必须编写自己的有效成员资格测试,该测试利用队列的内部结构(可以在O(log n)中完成,我想说…)。有一个缺点:提取排序列表的复杂度为O(n log n)

Though it does not (yet) provide a custom search function, the heapq module may suit your needs. It implements a heap queue using a regular list. You’d have to write your own efficient membership test that makes use of the queue’s internal structure (that can be done in O(log n), I’d say…). There is one downside: extracting a sorted list has complexity O(n log n).


回答 5

我会使用biscectsortedcontainers模块。我确实没有经验,但是我认为该heapq模块有效。它包含一个Heap Queue

I would use the biscect or sortedcontainers modules. I don’t really am experienced, but I think the heapq module works. It contains a Heap Queue


回答 6

在Python上实现您自己的排序列表可能并不困难。以下是概念证明:

import bisect

class sortlist:
    def __init__(self, list):
        self.list = list
        self.sort()
    def sort(self):
        l = []
        for i in range(len(self.list)):
            bisect.insort(l, self.list[i])
        self.list = l
        self.len = i
    def insert(self, value):
        bisect.insort(self.list, value)
        self.len += 1
    def show(self):
        print self.list
    def search(self,value):
        left = bisect.bisect_left(self.list, value)
        if abs(self.list[min([left,self.len-1])] - value) >= abs(self.list[left-1] - value):
            return self.list[left-1]
        else:
            return self.list[left]

list = [101, 3, 10, 14, 23, 86, 44, 45, 45, 50, 66, 95, 17, 77, 79, 84, 85, 91, 73]
slist = sortlist(list)
slist.show()
slist.insert(99)
slist.show()
print slist.search(100000000)
print slist.search(0)
print slist.search(56.7)

=========结果============

[3、10、14、17、23、44、45、45、50、66、73、77、79、84、85、86、91、95、101]

[3、10、14、17、23、44、45、45、50、66、73、77、79、84、85、86、91、95、99、101]

101

3

50

It may not be hard to implement your own sortlist on Python. Below is a proof of concept:

import bisect

class sortlist:
    def __init__(self, list):
        self.list = list
        self.sort()
    def sort(self):
        l = []
        for i in range(len(self.list)):
            bisect.insort(l, self.list[i])
        self.list = l
        self.len = i
    def insert(self, value):
        bisect.insort(self.list, value)
        self.len += 1
    def show(self):
        print self.list
    def search(self,value):
        left = bisect.bisect_left(self.list, value)
        if abs(self.list[min([left,self.len-1])] - value) >= abs(self.list[left-1] - value):
            return self.list[left-1]
        else:
            return self.list[left]

list = [101, 3, 10, 14, 23, 86, 44, 45, 45, 50, 66, 95, 17, 77, 79, 84, 85, 91, 73]
slist = sortlist(list)
slist.show()
slist.insert(99)
slist.show()
print slist.search(100000000)
print slist.search(0)
print slist.search(56.7)

========= Results ============

[3, 10, 14, 17, 23, 44, 45, 45, 50, 66, 73, 77, 79, 84, 85, 86, 91, 95, 101]

[3, 10, 14, 17, 23, 44, 45, 45, 50, 66, 73, 77, 79, 84, 85, 86, 91, 95, 99, 101]

101

3

50


在RHEL上安装Python 3

问题:在RHEL上安装Python 3

我正在尝试使用以下步骤在RHEL上安装python3:

yum search python3

哪个回来了 No matches found for: python3

其次是:

yum search python

搜索结果均未包含python3。接下来我应该尝试什么?

I’m trying to install python3 on RHEL using the following steps:

yum search python3

Which returned No matches found for: python3

Followed by:

yum search python

None of the search results contained python3. What should I try next?


回答 0

手动安装很容易:

  1. 下载(Python.org上可能有较新的版本):

    $ wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tar.xz
  2. 解压缩

    $ tar xf Python-3.* 
    $ cd Python-3.*
  3. 准备编译

    $ ./configure
  4. 建立

    $ make
  5. 安装

    $ make install

    或者,如果您不想覆盖python可执行文件(更安全,至少在某些发行版中yum需要python为2.x,例如RHEL6)-您可以python3.*使用并发实例安装到系统默认值altinstall

    $ make altinstall

现在,如果要使用备用安装目录,则可以传递--prefixconfigure命令。

示例:要在/ opt / local中“安装” Python,只需添加--prefix=/opt/local

在后make install步:为了使用新的Python安装,它可能是,你还是要在[前缀] / bin加入到$PATH和[前缀] / lib下的$LD_LIBRARY_PATH(根据的--prefix你通过)

It is easy to install it manually:

  1. Download (there may be newer releases on Python.org):

    $ wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tar.xz
    
  2. Unzip

    $ tar xf Python-3.* 
    $ cd Python-3.*
    
  3. Prepare compilation

    $ ./configure
    
  4. Build

    $ make
    
  5. Install

    $ make install
    

    OR if you don’t want to overwrite the python executable (safer, at least on some distros yum needs python to be 2.x, such as for RHEL6) – you can install python3.* as a concurrent instance to the system default with an altinstall:

    $ make altinstall
    

Now if you want an alternative installation directory, you can pass --prefix to the configurecommand.

Example: for ‘installing’ Python in /opt/local, just add --prefix=/opt/local.

After the make install step: In order to use your new Python installation, it could be, that you still have to add the [prefix]/bin to the $PATH and [prefix]/lib to the $LD_LIBRARY_PATH (depending of the --prefix you passed)


回答 1

从RPM安装通常更好,因为:

  • 您可以安装和卸载(正确)python3。
  • 安装时间的方式更快。如果您在具有多个VM的云环境中工作,则不能在每个VM上编译python3。

解决方案1:Red Hat和EPEL存储库

红帽通过EPEL存储库添加了:

  • 适用于CentOS 6的Python 3.4
  • 适用于CentOS 7的Python 3.6

[EPEL]如何在CentOS 6上安装Python 3.4

sudo yum install -y epel-release
sudo yum install -y python34

# Install pip3
sudo yum install -y python34-setuptools  # install easy_install-3.4
sudo easy_install-3.4 pip

您可以使用以下命令创建您的virtualenvpyvenv

pyvenv /tmp/foo

[EPEL]如何在CentOS 7上安装Python 3.6

与CentOS7一起pip3.6提供:)

sudo yum install -y epel-release
sudo yum install -y python36 python36-pip

您可以使用以下命令创建您的virtualenvpyvenv

python3.6 -m venv /tmp/foo

如果使用pyvenv脚本,则会收到警告:

$ pyvenv-3.6 /tmp/foo
WARNING: the pyenv script is deprecated in favour of `python3.6 -m venv`

解决方案2:IUS社区存储库

IUS社区提供了一些 有关RHEL和CentOS的最新软件包。后面的人来自Rackspace,所以我认为他们值得信赖…

https://ius.io/

在此处为您检查正确的仓库:

https://ius.io/setup

[IUS]如何在CentOS 6上安装Python 3.6

sudo yum install -y https://repo.ius.io/ius-release-el6.rpm
sudo yum install -y python36u python36u-pip

您可以使用以下命令创建您的virtualenvpyvenv

python3.6 -m venv /tmp/foo

[IUS]如何在CentOS 7上安装Python 3.6

sudo yum install -y https://repo.ius.io/ius-release-el7.rpm
sudo yum install -y python36u python36u-pip

您可以使用以下命令创建您的virtualenvpyvenv

python3.6 -m venv /tmp/foo

Installing from RPM is generally better, because:

  • you can install and uninstall (properly) python3.
  • the installation time is way faster. If you work in a cloud environment with multiple VMs, compiling python3 on each VMs is not acceptable.

Solution 1: Red Hat & EPEL repositories

Red Hat has added through the EPEL repository:

  • Python 3.4 for CentOS 6
  • Python 3.6 for CentOS 7

[EPEL] How to install Python 3.4 on CentOS 6

sudo yum install -y epel-release
sudo yum install -y python34

# Install pip3
sudo yum install -y python34-setuptools  # install easy_install-3.4
sudo easy_install-3.4 pip

You can create your virtualenv using pyvenv:

pyvenv /tmp/foo

[EPEL] How to install Python 3.6 on CentOS 7

With CentOS7, pip3.6 is provided as a package :)

sudo yum install -y epel-release
sudo yum install -y python36 python36-pip

You can create your virtualenv using pyvenv:

python3.6 -m venv /tmp/foo

If you use the pyvenv script, you’ll get a WARNING:

$ pyvenv-3.6 /tmp/foo
WARNING: the pyenv script is deprecated in favour of `python3.6 -m venv`

Solution 2: IUS Community repositories

The IUS Community provides some up-to-date packages for RHEL & CentOS. The guys behind are from Rackspace, so I think that they are quite trustworthy…

https://ius.io/

Check the right repo for you here:

https://ius.io/setup

[IUS] How to install Python 3.6 on CentOS 6

sudo yum install -y https://repo.ius.io/ius-release-el6.rpm
sudo yum install -y python36u python36u-pip

You can create your virtualenv using pyvenv:

python3.6 -m venv /tmp/foo

[IUS] How to install Python 3.6 on CentOS 7

sudo yum install -y https://repo.ius.io/ius-release-el7.rpm
sudo yum install -y python36u python36u-pip

You can create your virtualenv using pyvenv:

python3.6 -m venv /tmp/foo

回答 2

除了gecco的答案外,我还将步骤3更改为:

./configure

至:

./configure --prefix=/opt/python3

然后,在安装后,您还可以:

# ln -s /opt/python3/bin/python3 /usr/bin/python3

这是为了确保安装不会与yum安装的python冲突。

请参阅我在Internet上找到的说明:

http://www.hosting.com/support/linux/installing-python-3-on-centosredhat-5x-from-source

In addition to gecco’s answer I would change step 3 from:

./configure

to:

./configure --prefix=/opt/python3

Then after installation you could also:

# ln -s /opt/python3/bin/python3 /usr/bin/python3

It is to ensure that installation will not conflict with python installed with yum.

See explanation I have found on Internet:

http://www.hosting.com/support/linux/installing-python-3-on-centosredhat-5x-from-source


回答 3

与Python 2.7和3.3一起,Red Hat Software Collections现在包括Python 3.4-均可在RHEL 6和7上运行。

RHSCL 2.0文档位于https://access.redhat.com/documentation/zh-CN/Red_Hat_Software_Collections/

加上developerblog.redhat.com上的许多文章。

编辑

请按照以下说明在RHEL 6/7或CentOS 6/7上安装Python 3.4:

# 1. Install the Software Collections tools:
yum install scl-utils

# 2. Download a package with repository for your system.
#  (See the Yum Repositories on external link. For RHEL/CentOS 6:)
wget https://www.softwarecollections.org/en/scls/rhscl/rh-python34/epel-6-x86_64/download/rhscl-rh-python34-epel-6-x86_64.noarch.rpm
#  or for RHEL/CentOS 7
wget https://www.softwarecollections.org/en/scls/rhscl/rh-python34/epel-7-x86_64/download/rhscl-rh-python34-epel-7-x86_64.noarch.rpm

# 3. Install the repo package (on RHEL you will need to enable optional channel first):
yum install rhscl-rh-python34-*.noarch.rpm

# 4. Install the collection:
yum install rh-python34

# 5. Start using software collections:
scl enable rh-python34 bash

Along with Python 2.7 and 3.3, Red Hat Software Collections now includes Python 3.4 – all work on both RHEL 6 and 7.

RHSCL 2.0 docs are at https://access.redhat.com/documentation/en-US/Red_Hat_Software_Collections/

Plus lot of articles at developerblog.redhat.com.

edit

Follow these instructions to install Python 3.4 on RHEL 6/7 or CentOS 6/7:

# 1. Install the Software Collections tools:
yum install scl-utils

# 2. Download a package with repository for your system.
#  (See the Yum Repositories on external link. For RHEL/CentOS 6:)
wget https://www.softwarecollections.org/en/scls/rhscl/rh-python34/epel-6-x86_64/download/rhscl-rh-python34-epel-6-x86_64.noarch.rpm
#  or for RHEL/CentOS 7
wget https://www.softwarecollections.org/en/scls/rhscl/rh-python34/epel-7-x86_64/download/rhscl-rh-python34-epel-7-x86_64.noarch.rpm

# 3. Install the repo package (on RHEL you will need to enable optional channel first):
yum install rhscl-rh-python34-*.noarch.rpm

# 4. Install the collection:
yum install rh-python34

# 5. Start using software collections:
scl enable rh-python34 bash

回答 4

使用SCL存储库。

sudo sh -c 'wget -qO- http://people.redhat.com/bkabrda/scl_python33.repo >> /etc/yum.repos.d/scl.repo'
sudo yum install python33
scl enable python27

(每当您要使用python27而不是系统默认值时,都必须运行此最后一个命令。)

Use the SCL repos.

sudo sh -c 'wget -qO- http://people.redhat.com/bkabrda/scl_python33.repo >> /etc/yum.repos.d/scl.repo'
sudo yum install python33
scl enable python27

(This last command will have to be run each time you want to use python27 rather than the system default.)


回答 5

Python3最近作为Python34被添加到EPEL7中。

当前(正在进行)正在努力制定有关如何在EPEL7中打包Python3的打包准则。

参见https://bugzilla.redhat.com/show_bug.cgi?id=1219411
https://lists.fedoraproject.org/pipermail/python-devel/2015-July/000721.html

Python3 was recently added to EPEL7 as Python34.

There is ongoing (currently) effort to make packaging guidelines about how to package things for Python3 in EPEL7.

See https://bugzilla.redhat.com/show_bug.cgi?id=1219411
and https://lists.fedoraproject.org/pipermail/python-devel/2015-July/000721.html


回答 6

您可以从此处下载RHEL6 / CentOS6的源RPM和二进制RPM。

这是从最新的Fedora开发源rpm到RHEL6 / CentOS6的反向移植

You can download a source RPMs and binary RPMs for RHEL6 / CentOS6 from here

This is a backport from the newest Fedora development source rpm to RHEL6 / CentOS6


回答 7

我看到的所有答案都是要求从代码中编译python3或安装二进制RPM软件包。这是启用EPEL(企业Linux的额外软件包)然后使用yum安装python的另一个答案。RHEL 7.5(Maipo)的步骤

yum install wget y
wget https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-7-11.noarch.rpm
rpm ivh epel-*.rpm
yum install python36

另请参阅链接

I see all the answers as either asking to compile python3 from code or installing the binary RPM package. Here is another answer to enable EPEL (Extra Packages for Enterprise Linux) and then install python using yum. Steps for RHEL 7.5 (Maipo)

yum install wget –y
wget https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-7-XX.noarch.rpm # Verify actual RPM name by browsing dir over browser
rpm –ivh epel-*.rpm
yum install python36

Also see link


回答 8

我在使用python 2.7时遇到了同样的问题。请按照以下步骤成功升级到3.6。您也可以尝试以下一种

  1. 升级版本为2.x之前查看

    python --version
    Python 2.7.5
  2. 使用以下命令将python升级到3.x版本-

    百胜安装python3x

    用所需的版本号替换x

    即用于安装python 3.6执行

    yum install python36
  3. 之后,如果您想将此Python设置为默认版本,则在bashrc文件中添加

    vi〜/ .bashrc

    alias python='python3.6'
  4. 执行bash命令以应用设置

    bash 
  5. 现在您可以看到以下版本

    python --version
    Python 3.6.3

I was having the same issue using the python 2.7. Follow the below steps to upgrade successfully to 3.6. You can also try this one-

  1. See before upgrading version is 2.x

    python --version
    Python 2.7.5
    
  2. Use below command to upgrade your python to 3.x version-

    yum install python3x

    replace x with the version number you want.

    i.e. for installing python 3.6 execute

    yum install python36
    
  3. After that if you want to set this python for your default version then in bashrc file add

    vi ~/.bashrc

    alias python='python3.6'
    
  4. execute bash command to apply the settings

    bash 
    
  5. Now you can see the version below

    python --version
    Python 3.6.3
    

回答 9

通过Software Collections使用Python 3.5的三个步骤:

sudo yum install centos-release-scl
sudo yum install rh-python35
scl enable rh-python35 bash

请注意,最后一个命令不需要sudo。现在我们可以看到python 3是当前shell的默认值:

python --version
Python 3.5.1

如果您希望将Python 2作为当前shell的默认设置,则只需跳过最后一个命令。

现在,假设您的Python 3脚本给您一个类似的错误/usr/bin/env: python3: No such file or directory。这是因为安装通常是通过不寻常的路径完成的:

/opt/rh/rh-python35/root/bin/python3

以上通常是符号链接。如果要在启动时python3自动$PATH为所有用户添加到,则一种添加方法是添加以下文件:

sudo vim /etc/profile.d/rh-python35.sh

会有这样的东西:

#!/bin/bash

PATH=$PATH:/opt/rh/rh-python35/root/bin/

现在,重新启动后,如果我们这样做

python3 --version

它应该工作。一个exceptions是自动生成的用户,例如没有外壳的Jenkins服务器中的“ jenkins”。在这种情况下,手动添加路径到$PATH脚本将是一种方法。

最后,如果您正在使用sudo pip3安装软件包,但是它告诉您找不到pip3,则可能是您在/ etc / sudoers中有一个secure_path。检查应确认这一点。要在运行命令时临时使用标准PATH,可以执行以下操作:sudo visudo

sudo env "PATH=$PATH" pip3 --version

有关更多详细信息,请参见此问题。

注意:Software Collections有一个更新的Python 3.6,但是由于我在尝试安装Pycurl时遇到了很多麻烦,因此目前不推荐使用。对于Python 3.5来说,这不是问题,因为我刚sudo yum install sclo-python35-python-pycurl做完了就可以了。

Three steps using Python 3.5 by Software Collections:

sudo yum install centos-release-scl
sudo yum install rh-python35
scl enable rh-python35 bash

Note that sudo is not needed for the last command. Now we can see that python 3 is the default for the current shell:

python --version
Python 3.5.1

Simply skip the last command if you’d rather have Python 2 as the default for the current shell.

Now let’s say that your Python 3 scripts give you an error like /usr/bin/env: python3: No such file or directory. That’s because the installation is usually done to an unusual path:

/opt/rh/rh-python35/root/bin/python3

The above would normally be a symlink. If you want python3 to be automatically added to the $PATH for all users on startup, one way to do this is adding a file like:

sudo vim /etc/profile.d/rh-python35.sh

Which would have something like:

#!/bin/bash

PATH=$PATH:/opt/rh/rh-python35/root/bin/

And now after a reboot, if we do

python3 --version

It should just work. One exception would be an auto-generated user like “jenkins” in a Jenkins server which doesn’t have a shell. In that case, manually adding the path to $PATH in scripts would be one way to go.

Finally, if you’re using sudo pip3 to install packages, but it tells you that pip3 cannot be found, it could be that you have a secure_path in /etc/sudoers. Checking with sudo visudo should confirm that. To temporarily use the standard PATH when running commands you can do, for example:

sudo env "PATH=$PATH" pip3 --version

See this question for more details.

NOTE: There is a newer Python 3.6 by Software Collections, but I wouldn’t recommend it at this time, because I had major headaches trying to install Pycurl. For Python 3.5 that isn’t an issue because I just did sudo yum install sclo-python35-python-pycurl which worked out of the box.


回答 10

如果您使用的是RHEL,并且希望使用Red Hat支持的Python,请使用Red Hat软件集合(RHSCL)。Red Hat不支持EPEL和IUS软件包。上面的许多答案也指向CentOS软件集合。虽然可以安装它们,但它们不是Red Hat支持的RHEL软件包。

另外,票数最高的答案也提供了不好的建议-在RHEL上,您不想更改/usr/bin/python/usr/bin/python2因为您可能会破坏yum和使用其他RHEL管理工具。看一下/bin/yum,它是一个以开头的Python脚本#!/usr/bin/python。如果您从源代码编译Python,请不要make install以root用户身份进行操作。那将覆盖/usr/bin/python。如果中断yum,则很难恢复系统。

欲了解更多信息,请参阅如何安装Python 3,画中画,VENV,virtualenv中,并pipenv在RHELdevelopers.redhat.com。它涵盖了从RHSCL安装和使用Python 3,使用Python虚拟环境以及使用软件集合以及在RHEL上使用Python的许多技巧。

简而言之,要通过Red Hat Software Collections安装Python 3.6:

$ su -
# subscription-manager repos --enable rhel-7-server-optional-rpms \
   --enable rhel-server-rhscl-7-rpms
# yum -y install @development
# yum -y install rh-python36

# yum -y install rh-python36-numpy \
   rh-python36-scipy \ 
   rh-python36-python-tools \
   rh-python36-python-six

要使用软件集合,您必须启用它:

scl enable rh-python36 bash

但是,如果要永久启用Python 3,可以将以下内容添加到〜/ .bashrc中,然后注销并重新登录。现在,Python 3永久存在。

# Add RHSCL Python 3 to my login environment
source scl_source enable rh-python36

注意:执行此操作后,python现在输入即可提供Python 3.6而不是Python 2.7。

有关更多信息,请参见上面的文章。

If you are on RHEL and want a Red Hat supported Python, use Red Hat Software collections (RHSCL). The EPEL and IUS packages are not supported by Red Hat. Also many of the answers above point to the CentOS software collections. While you can install those, they aren’t the Red Hat supported packages for RHEL.

Also, the top voted answer gives bad advice – On RHEL you do not want to change /usr/bin/python, /usr/bin/python2 because you will likely break yum and other RHEL admin tools. Take a look at /bin/yum, it is a Python script that starts with #!/usr/bin/python. If you compile Python from source, do not do a make install as root. That will overwrite /usr/bin/python. If you break yum it can be difficult to restore your system.

For more info, see How to install Python 3, pip, venv, virtualenv, and pipenv on RHEL on developers.redhat.com. It covers installing and using Python 3 from RHSCL, using Python Virtual Environments, and a number of tips for working with software collections and working with Python on RHEL.

In a nutshell, to install Python 3.6 via Red Hat Software Collections:

$ su -
# subscription-manager repos --enable rhel-7-server-optional-rpms \
   --enable rhel-server-rhscl-7-rpms
# yum -y install @development
# yum -y install rh-python36

# yum -y install rh-python36-numpy \
   rh-python36-scipy \ 
   rh-python36-python-tools \
   rh-python36-python-six

To use a software collection you have to enable it:

scl enable rh-python36 bash

However if you want Python 3 permanently enabled, you can add the following to your ~/.bashrc and then log out and back in again. Now Python 3 is permanently in your path.

# Add RHSCL Python 3 to my login environment
source scl_source enable rh-python36

Note: once you do that, typing python now gives you Python 3.6 instead of Python 2.7.

See the above article for all of this and a lot more detail.


回答 11

如果您需要正式的RHEL软件包,可以使用RHSCL(红帽软件集合)

更多细节:

您必须有权访问Red Hat Customer Portal才能阅读全文。

If you want official RHEL packages you can use RHSCL (Red Hat Software Collections)

More details:

You have to have access to Red Hat Customer Portal to read full articles.


回答 12

这是我按照以下步骤安装Python3的步骤:

yum install wget
wget https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tar.xz  
sudo tar xvf Python-3.*   
cd Python-3.* 
sudo ./configure --prefix=/opt/python3    
sudo make   
sudo make install   
sudo ln -s /opt/python3/bin/python3 /usr/bin/python3

$ /usr/bin/python3    
Python 3.6.0

Here are the steps i followed to install Python3:

yum install wget
wget https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tar.xz  
sudo tar xvf Python-3.*   
cd Python-3.* 
sudo ./configure --prefix=/opt/python3    
sudo make   
sudo make install   
sudo ln -s /opt/python3/bin/python3 /usr/bin/python3

$ /usr/bin/python3    
Python 3.6.0

回答 13

yum install python34.x86_64如果您已epel-release安装,则可以正常工作,此答案说明了如何操作,并且我确认它可以正常工作RHEL 7.3

$ cat /etc/*-release
NAME="Red Hat Enterprise Linux Server"
VERSION="7.3 (Maipo)

$ type python3
python3 is hashed (/usr/bin/python3)

yum install python34.x86_64 works if you have epel-release installed, which this answer explains how to, and I confirmed it worked on RHEL 7.3

$ cat /etc/*-release
NAME="Red Hat Enterprise Linux Server"
VERSION="7.3 (Maipo)

$ type python3
python3 is hashed (/usr/bin/python3)

回答 14

对于Amazon Linux上的RHEL,必须使用python3:

须藤百胜安装python34-devel

For RHEL on Amazon Linux, using python3 I had to do :

sudo yum install python34-devel


回答 15

当SCL不可用时,完全工作36(基于Joys输入)

yum install wget y
wget https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-7-11.noarch.rpm
rpm ivh epel-*.rpm
yum install python36

sudo yum install python34-setuptools
sudo mkdir /usr/local/lib/python3.6
sudo mkdir /usr/local/lib/python3.6/site-packages

sudo easy_install-3.6 pip

最后激活环境…

pyvenv-3.6 py3
source py3/bin/activate

然后python3

Full working 36 when SCL is not available (based on Joys input)

yum install wget –y
wget https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-7-11.noarch.rpm
rpm –ivh epel-*.rpm
yum install python36

sudo yum install python34-setuptools
sudo mkdir /usr/local/lib/python3.6
sudo mkdir /usr/local/lib/python3.6/site-packages

sudo easy_install-3.6 pip

Finally activate the environment…

pyvenv-3.6 py3
source py3/bin/activate

Then python3


回答 16

您可以安装miniconda(https://conda.io/miniconda.html)。这不仅仅是python 3.7,但安装非常简单明了。

curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O
sudo yum install bzip2
bash Miniconda3-latest-Linux-x86_64.sh

您必须接受许可协议,并在交互模式下选择一些选项(接受默认值)。我相信它也可以以某种方式静默安装。

You can install miniconda (https://conda.io/miniconda.html). That’s a bit more than just python 3.7 but the installation is very straightforward and simple.

curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O
sudo yum install bzip2
bash Miniconda3-latest-Linux-x86_64.sh

You’ll have to accept the license agreement and choose some options in interactive mode (accept the defaults). I believe it can be also installed silently somehow.


回答 17

对于使用AWS EC2 RHEL 7.5的用户,(使用sudo)启用必需的存储库

yum-config-manager --enable rhui-REGION-rhel-server-optional
yum-config-manager --enable rhui-REGION-rhel-server-rhscl

安装Python 3.6

yum install rh-python36

安装其他依赖项

yum install rh-python36-numpy  rh-python36-scipy  rh-python36-python-tools  rh-python36-python-six

For those working on AWS EC2 RHEL 7.5, (use sudo) enable required repos

yum-config-manager --enable rhui-REGION-rhel-server-optional
yum-config-manager --enable rhui-REGION-rhel-server-rhscl

Install Python 3.6

yum install rh-python36

Install other dependencies

yum install rh-python36-numpy  rh-python36-scipy  rh-python36-python-tools  rh-python36-python-six

部分字符串格式

问题:部分字符串格式

是否可以使用类似于字符串模板safe_substitute()功能的高级字符串格式化方法来进行部分字符串格式化?

例如:

s = '{foo} {bar}'
s.format(foo='FOO') #Problem: raises KeyError 'bar'

Is it possible to do partial string formatting with the advanced string formatting methods, similar to the string template safe_substitute() function?

For example:

s = '{foo} {bar}'
s.format(foo='FOO') #Problem: raises KeyError 'bar'

回答 0

您可以通过覆盖映射将其欺骗为部分格式:

import string

class FormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"

s = '{foo} {bar}'
formatter = string.Formatter()
mapping = FormatDict(foo='FOO')
print(formatter.vformat(s, (), mapping))

印刷

FOO {bar}

当然,此基本实现仅适用于基本情况。

You can trick it into partial formatting by overwriting the mapping:

import string

class FormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"

s = '{foo} {bar}'
formatter = string.Formatter()
mapping = FormatDict(foo='FOO')
print(formatter.vformat(s, (), mapping))

printing

FOO {bar}

Of course this basic implementation only works correctly for basic cases.


回答 1

如果您知道要格式化的顺序:

s = '{foo} {{bar}}'

像这样使用它:

ss = s.format(foo='FOO') 
print ss 
>>> 'FOO {bar}'

print ss.format(bar='BAR')
>>> 'FOO BAR'

您不能指定foo,并bar在同一时间-你必须按顺序做。

If you know in what order you’re formatting things:

s = '{foo} {{bar}}'

Use it like this:

ss = s.format(foo='FOO') 
print ss 
>>> 'FOO {bar}'

print ss.format(bar='BAR')
>>> 'FOO BAR'

You can’t specify foo and bar at the same time – you have to do it sequentially.


回答 2

您可以使用简短易读的partial函数functools,并描述编码器的意图:

from functools import partial

s = partial("{foo} {bar}".format, foo="FOO")
print s(bar="BAR")
# FOO BAR

You could use the partial function from functools which is short, most readable and also describes the coder’s intention:

from functools import partial

s = partial("{foo} {bar}".format, foo="FOO")
print s(bar="BAR")
# FOO BAR

回答 3

这种局限性.format()-无法进行部分替换-使我烦恼。

在评估了编写一个自定义Formatter类(如此处许多答案所述)之后,甚至考虑使用第三方包(例如lazy_format),我发现了一个更简单的内置解决方案:模板字符串

它提供了相似的功能,但也提供了部分替代彻底的safe_substitute()方法。模板字符串需要有一个$前缀(感觉有些奇怪-但我认为总体解决方案更好)。

import string
template = string.Template('${x} ${y}')
try:
  template.substitute({'x':1}) # raises KeyError
except KeyError:
  pass

# but the following raises no error
partial_str = template.safe_substitute({'x':1}) # no error

# partial_str now contains a string with partial substitution
partial_template = string.Template(partial_str)
substituted_str = partial_template.safe_substitute({'y':2}) # no error
print substituted_str # prints '12'

基于此形成了一个便捷包装器:

class StringTemplate(object):
    def __init__(self, template):
        self.template = string.Template(template)
        self.partial_substituted_str = None

    def __repr__(self):
        return self.template.safe_substitute()

    def format(self, *args, **kws):
        self.partial_substituted_str = self.template.safe_substitute(*args, **kws)
        self.template = string.Template(self.partial_substituted_str)
        return self.__repr__()


>>> s = StringTemplate('${x}${y}')
>>> s
'${x}${y}'
>>> s.format(x=1)
'1${y}'
>>> s.format({'y':2})
'12'
>>> print s
12

同样,基于Sven的答案的包装器使用默认的字符串格式:

class StringTemplate(object):
    class FormatDict(dict):
        def __missing__(self, key):
            return "{" + key + "}"

    def __init__(self, template):
        self.substituted_str = template
        self.formatter = string.Formatter()

    def __repr__(self):
        return self.substituted_str

    def format(self, *args, **kwargs):
        mapping = StringTemplate.FormatDict(*args, **kwargs)
        self.substituted_str = self.formatter.vformat(self.substituted_str, (), mapping)

This limitation of .format() – the inability to do partial substitutions – has been bugging me.

After evaluating writing a custom Formatter class as described in many answers here and even considering using third-party packages such as lazy_format, I discovered a much simpler inbuilt solution: Template strings

It provides similar functionality but also provides partial substitution thorough safe_substitute() method. The template strings need to have a $ prefix (which feels a bit weird – but the overall solution I think is better).

import string
template = string.Template('${x} ${y}')
try:
  template.substitute({'x':1}) # raises KeyError
except KeyError:
  pass

# but the following raises no error
partial_str = template.safe_substitute({'x':1}) # no error

# partial_str now contains a string with partial substitution
partial_template = string.Template(partial_str)
substituted_str = partial_template.safe_substitute({'y':2}) # no error
print substituted_str # prints '12'

Formed a convenience wrapper based on this:

class StringTemplate(object):
    def __init__(self, template):
        self.template = string.Template(template)
        self.partial_substituted_str = None

    def __repr__(self):
        return self.template.safe_substitute()

    def format(self, *args, **kws):
        self.partial_substituted_str = self.template.safe_substitute(*args, **kws)
        self.template = string.Template(self.partial_substituted_str)
        return self.__repr__()


>>> s = StringTemplate('${x}${y}')
>>> s
'${x}${y}'
>>> s.format(x=1)
'1${y}'
>>> s.format({'y':2})
'12'
>>> print s
12

Similarly a wrapper based on Sven’s answer which uses the default string formatting:

class StringTemplate(object):
    class FormatDict(dict):
        def __missing__(self, key):
            return "{" + key + "}"

    def __init__(self, template):
        self.substituted_str = template
        self.formatter = string.Formatter()

    def __repr__(self):
        return self.substituted_str

    def format(self, *args, **kwargs):
        mapping = StringTemplate.FormatDict(*args, **kwargs)
        self.substituted_str = self.formatter.vformat(self.substituted_str, (), mapping)

回答 4

不知道这是否可以作为一种快速的解决方法,但是如何解决

s = '{foo} {bar}'
s.format(foo='FOO', bar='{bar}')

?:)

Not sure if this is ok as a quick workaround, but how about

s = '{foo} {bar}'
s.format(foo='FOO', bar='{bar}')

? :)


回答 5

如果您定义自己Formatterget_value方法,则可以使用该方法将未定义的字段名称映射到所需的任何内容:

http://docs.python.org/library/string.html#string.Formatter.get_value

例如,您可以映射bar"{bar}"如果bar不在kwargs中。

但是,这需要使用format()Formatter对象的format()方法,而不是字符串的方法。

If you define your own Formatter which overrides the get_value method, you could use that to map undefined field names to whatever you wanted:

http://docs.python.org/library/string.html#string.Formatter.get_value

For instance, you could map bar to "{bar}" if bar isn’t in the kwargs.

However, that requires using the format() method of your Formatter object, not the string’s format() method.


回答 6

>>> 'fd:{uid}:{{topic_id}}'.format(uid=123)
'fd:123:{topic_id}'

试试看

>>> 'fd:{uid}:{{topic_id}}'.format(uid=123)
'fd:123:{topic_id}'

Try this out.


回答 7

感谢Amber的评论,我想到了这一点:

import string

try:
    # Python 3
    from _string import formatter_field_name_split
except ImportError:
    formatter_field_name_split = str._formatter_field_name_split


class PartialFormatter(string.Formatter):
    def get_field(self, field_name, args, kwargs):
        try:
            val = super(PartialFormatter, self).get_field(field_name, args, kwargs)
        except (IndexError, KeyError, AttributeError):
            first, _ = formatter_field_name_split(field_name)
            val = '{' + field_name + '}', first
        return val

Thanks to Amber‘s comment, I came up with this:

import string

try:
    # Python 3
    from _string import formatter_field_name_split
except ImportError:
    formatter_field_name_split = str._formatter_field_name_split


class PartialFormatter(string.Formatter):
    def get_field(self, field_name, args, kwargs):
        try:
            val = super(PartialFormatter, self).get_field(field_name, args, kwargs)
        except (IndexError, KeyError, AttributeError):
            first, _ = formatter_field_name_split(field_name)
            val = '{' + field_name + '}', first
        return val

回答 8

对我来说这已经足够了:

>>> ss = 'dfassf {} dfasfae efaef {} fds'
>>> nn = ss.format('f1', '{}')
>>> nn
'dfassf f1 dfasfae efaef {} fds'
>>> n2 = nn.format('whoa')
>>> n2
'dfassf f1 dfasfae efaef whoa fds'

For me this was good enough:

>>> ss = 'dfassf {} dfasfae efaef {} fds'
>>> nn = ss.format('f1', '{}')
>>> nn
'dfassf f1 dfasfae efaef {} fds'
>>> n2 = nn.format('whoa')
>>> n2
'dfassf f1 dfasfae efaef whoa fds'

回答 9

我发现的所有解决方案似乎在使用更高级的规格或转换选项方面均存在问题。@SvenMarnach的FormatPlaceholder非常聪明,但是它不能与强制(例如{a!s:>2s})一起正常使用,因为它调用了__str__方法(在此示例中),而不是调用该方法,__format__并且您丢失了任何其他格式。

这是我最终得到的结果,以及其中的一些关键功能:

sformat('The {} is {}', 'answer')
'The answer is {}'

sformat('The answer to {question!r} is {answer:0.2f}', answer=42)
'The answer to {question!r} is 42.00'

sformat('The {} to {} is {:0.{p}f}', 'answer', 'everything', p=4)
'The answer to everything is {:0.4f}'
  • 提供类似的界面str.format(不只是映射)
  • 支持更复杂的格式选项:
    • 强迫 {k!s} {!r}
    • 套料 {k:>{size}}
    • getattr {k.foo}
    • getitem {k[0]}
    • 强制+格式 {k!s:>{size}}
import string


class SparseFormatter(string.Formatter):
    """
    A modified string formatter that handles a sparse set of format
    args/kwargs.
    """

    # re-implemented this method for python2/3 compatibility
    def vformat(self, format_string, args, kwargs):
        used_args = set()
        result, _ = self._vformat(format_string, args, kwargs, used_args, 2)
        self.check_unused_args(used_args, args, kwargs)
        return result

    def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
                 auto_arg_index=0):
        if recursion_depth < 0:
            raise ValueError('Max string recursion exceeded')
        result = []
        for literal_text, field_name, format_spec, conversion in \
                self.parse(format_string):

            orig_field_name = field_name

            # output the literal text
            if literal_text:
                result.append(literal_text)

            # if there's a field, output it
            if field_name is not None:
                # this is some markup, find the object and do
                #  the formatting

                # handle arg indexing when empty field_names are given.
                if field_name == '':
                    if auto_arg_index is False:
                        raise ValueError('cannot switch from manual field '
                                         'specification to automatic field '
                                         'numbering')
                    field_name = str(auto_arg_index)
                    auto_arg_index += 1
                elif field_name.isdigit():
                    if auto_arg_index:
                        raise ValueError('cannot switch from manual field '
                                         'specification to automatic field '
                                         'numbering')
                    # disable auto arg incrementing, if it gets
                    # used later on, then an exception will be raised
                    auto_arg_index = False

                # given the field_name, find the object it references
                #  and the argument it came from
                try:
                    obj, arg_used = self.get_field(field_name, args, kwargs)
                except (IndexError, KeyError):
                    # catch issues with both arg indexing and kwarg key errors
                    obj = orig_field_name
                    if conversion:
                        obj += '!{}'.format(conversion)
                    if format_spec:
                        format_spec, auto_arg_index = self._vformat(
                            format_spec, args, kwargs, used_args,
                            recursion_depth, auto_arg_index=auto_arg_index)
                        obj += ':{}'.format(format_spec)
                    result.append('{' + obj + '}')
                else:
                    used_args.add(arg_used)

                    # do any conversion on the resulting object
                    obj = self.convert_field(obj, conversion)

                    # expand the format spec, if needed
                    format_spec, auto_arg_index = self._vformat(
                        format_spec, args, kwargs,
                        used_args, recursion_depth-1,
                        auto_arg_index=auto_arg_index)

                    # format the object and append to the result
                    result.append(self.format_field(obj, format_spec))

        return ''.join(result), auto_arg_index


def sformat(s, *args, **kwargs):
    # type: (str, *Any, **Any) -> str
    """
    Sparse format a string.

    Parameters
    ----------
    s : str
    args : *Any
    kwargs : **Any

    Examples
    --------
    >>> sformat('The {} is {}', 'answer')
    'The answer is {}'

    >>> sformat('The answer to {question!r} is {answer:0.2f}', answer=42)
    'The answer to {question!r} is 42.00'

    >>> sformat('The {} to {} is {:0.{p}f}', 'answer', 'everything', p=4)
    'The answer to everything is {:0.4f}'

    Returns
    -------
    str
    """
    return SparseFormatter().format(s, *args, **kwargs)

在编写了一些有关如何使此方法运行的测试之后,我发现了各种实现的问题。如果有人发现他们有见识,它们就会在下面。

import pytest


def test_auto_indexing():
    # test basic arg auto-indexing
    assert sformat('{}{}', 4, 2) == '42'
    assert sformat('{}{} {}', 4, 2) == '42 {}'


def test_manual_indexing():
    # test basic arg indexing
    assert sformat('{0}{1} is not {1} or {0}', 4, 2) == '42 is not 2 or 4'
    assert sformat('{0}{1} is {3} {1} or {0}', 4, 2) == '42 is {3} 2 or 4'


def test_mixing_manualauto_fails():
    # test mixing manual and auto args raises
    with pytest.raises(ValueError):
        assert sformat('{!r} is {0}{1}', 4, 2)


def test_kwargs():
    # test basic kwarg
    assert sformat('{base}{n}', base=4, n=2) == '42'
    assert sformat('{base}{n}', base=4, n=2, extra='foo') == '42'
    assert sformat('{base}{n} {key}', base=4, n=2) == '42 {key}'


def test_args_and_kwargs():
    # test mixing args/kwargs with leftovers
    assert sformat('{}{k} {v}', 4, k=2) == '42 {v}'

    # test mixing with leftovers
    r = sformat('{}{} is the {k} to {!r}', 4, 2, k='answer')
    assert r == '42 is the answer to {!r}'


def test_coercion():
    # test coercion is preserved for skipped elements
    assert sformat('{!r} {k!r}', '42') == "'42' {k!r}"


def test_nesting():
    # test nesting works with or with out parent keys
    assert sformat('{k:>{size}}', k=42, size=3) == ' 42'
    assert sformat('{k:>{size}}', size=3) == '{k:>3}'


@pytest.mark.parametrize(
    ('s', 'expected'),
    [
        ('{a} {b}', '1 2.0'),
        ('{z} {y}', '{z} {y}'),
        ('{a} {a:2d} {a:04d} {y:2d} {z:04d}', '1  1 0001 {y:2d} {z:04d}'),
        ('{a!s} {z!s} {d!r}', '1 {z!s} {\'k\': \'v\'}'),
        ('{a!s:>2s} {z!s:>2s}', ' 1 {z!s:>2s}'),
        ('{a!s:>{a}s} {z!s:>{z}s}', '1 {z!s:>{z}s}'),
        ('{a.imag} {z.y}', '0 {z.y}'),
        ('{e[0]:03d} {z[0]:03d}', '042 {z[0]:03d}'),
    ],
    ids=[
        'normal',
        'none',
        'formatting',
        'coercion',
        'formatting+coercion',
        'nesting',
        'getattr',
        'getitem',
    ]
)
def test_sformat(s, expected):
    # test a bunch of random stuff
    data = dict(
        a=1,
        b=2.0,
        c='3',
        d={'k': 'v'},
        e=[42],
    )
    assert expected == sformat(s, **data)

All the solutions I’ve found seemed to have issues with more advanced spec or conversion options. @SvenMarnach’s FormatPlaceholder is wonderfully clever but it doesn’t work properly with coercion (e.g. {a!s:>2s}) because it calls the __str__ method (in this example) instead of __format__ and you lose any additional formatting.

Here’s what I ended up with and some of it’s key features:

sformat('The {} is {}', 'answer')
'The answer is {}'

sformat('The answer to {question!r} is {answer:0.2f}', answer=42)
'The answer to {question!r} is 42.00'

sformat('The {} to {} is {:0.{p}f}', 'answer', 'everything', p=4)
'The answer to everything is {:0.4f}'
  • provides similar interface as str.format (not just a mapping)
  • supports more complex formatting options:
    • coercion {k!s} {!r}
    • nesting {k:>{size}}
    • getattr {k.foo}
    • getitem {k[0]}
    • coercion+formatting {k!s:>{size}}
import string


class SparseFormatter(string.Formatter):
    """
    A modified string formatter that handles a sparse set of format
    args/kwargs.
    """

    # re-implemented this method for python2/3 compatibility
    def vformat(self, format_string, args, kwargs):
        used_args = set()
        result, _ = self._vformat(format_string, args, kwargs, used_args, 2)
        self.check_unused_args(used_args, args, kwargs)
        return result

    def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
                 auto_arg_index=0):
        if recursion_depth < 0:
            raise ValueError('Max string recursion exceeded')
        result = []
        for literal_text, field_name, format_spec, conversion in \
                self.parse(format_string):

            orig_field_name = field_name

            # output the literal text
            if literal_text:
                result.append(literal_text)

            # if there's a field, output it
            if field_name is not None:
                # this is some markup, find the object and do
                #  the formatting

                # handle arg indexing when empty field_names are given.
                if field_name == '':
                    if auto_arg_index is False:
                        raise ValueError('cannot switch from manual field '
                                         'specification to automatic field '
                                         'numbering')
                    field_name = str(auto_arg_index)
                    auto_arg_index += 1
                elif field_name.isdigit():
                    if auto_arg_index:
                        raise ValueError('cannot switch from manual field '
                                         'specification to automatic field '
                                         'numbering')
                    # disable auto arg incrementing, if it gets
                    # used later on, then an exception will be raised
                    auto_arg_index = False

                # given the field_name, find the object it references
                #  and the argument it came from
                try:
                    obj, arg_used = self.get_field(field_name, args, kwargs)
                except (IndexError, KeyError):
                    # catch issues with both arg indexing and kwarg key errors
                    obj = orig_field_name
                    if conversion:
                        obj += '!{}'.format(conversion)
                    if format_spec:
                        format_spec, auto_arg_index = self._vformat(
                            format_spec, args, kwargs, used_args,
                            recursion_depth, auto_arg_index=auto_arg_index)
                        obj += ':{}'.format(format_spec)
                    result.append('{' + obj + '}')
                else:
                    used_args.add(arg_used)

                    # do any conversion on the resulting object
                    obj = self.convert_field(obj, conversion)

                    # expand the format spec, if needed
                    format_spec, auto_arg_index = self._vformat(
                        format_spec, args, kwargs,
                        used_args, recursion_depth-1,
                        auto_arg_index=auto_arg_index)

                    # format the object and append to the result
                    result.append(self.format_field(obj, format_spec))

        return ''.join(result), auto_arg_index


def sformat(s, *args, **kwargs):
    # type: (str, *Any, **Any) -> str
    """
    Sparse format a string.

    Parameters
    ----------
    s : str
    args : *Any
    kwargs : **Any

    Examples
    --------
    >>> sformat('The {} is {}', 'answer')
    'The answer is {}'

    >>> sformat('The answer to {question!r} is {answer:0.2f}', answer=42)
    'The answer to {question!r} is 42.00'

    >>> sformat('The {} to {} is {:0.{p}f}', 'answer', 'everything', p=4)
    'The answer to everything is {:0.4f}'

    Returns
    -------
    str
    """
    return SparseFormatter().format(s, *args, **kwargs)

I discovered the issues with the various implementations after writing some tests on how I wanted this method to behave. They’re below if anyone finds them insightful.

import pytest


def test_auto_indexing():
    # test basic arg auto-indexing
    assert sformat('{}{}', 4, 2) == '42'
    assert sformat('{}{} {}', 4, 2) == '42 {}'


def test_manual_indexing():
    # test basic arg indexing
    assert sformat('{0}{1} is not {1} or {0}', 4, 2) == '42 is not 2 or 4'
    assert sformat('{0}{1} is {3} {1} or {0}', 4, 2) == '42 is {3} 2 or 4'


def test_mixing_manualauto_fails():
    # test mixing manual and auto args raises
    with pytest.raises(ValueError):
        assert sformat('{!r} is {0}{1}', 4, 2)


def test_kwargs():
    # test basic kwarg
    assert sformat('{base}{n}', base=4, n=2) == '42'
    assert sformat('{base}{n}', base=4, n=2, extra='foo') == '42'
    assert sformat('{base}{n} {key}', base=4, n=2) == '42 {key}'


def test_args_and_kwargs():
    # test mixing args/kwargs with leftovers
    assert sformat('{}{k} {v}', 4, k=2) == '42 {v}'

    # test mixing with leftovers
    r = sformat('{}{} is the {k} to {!r}', 4, 2, k='answer')
    assert r == '42 is the answer to {!r}'


def test_coercion():
    # test coercion is preserved for skipped elements
    assert sformat('{!r} {k!r}', '42') == "'42' {k!r}"


def test_nesting():
    # test nesting works with or with out parent keys
    assert sformat('{k:>{size}}', k=42, size=3) == ' 42'
    assert sformat('{k:>{size}}', size=3) == '{k:>3}'


@pytest.mark.parametrize(
    ('s', 'expected'),
    [
        ('{a} {b}', '1 2.0'),
        ('{z} {y}', '{z} {y}'),
        ('{a} {a:2d} {a:04d} {y:2d} {z:04d}', '1  1 0001 {y:2d} {z:04d}'),
        ('{a!s} {z!s} {d!r}', '1 {z!s} {\'k\': \'v\'}'),
        ('{a!s:>2s} {z!s:>2s}', ' 1 {z!s:>2s}'),
        ('{a!s:>{a}s} {z!s:>{z}s}', '1 {z!s:>{z}s}'),
        ('{a.imag} {z.y}', '0 {z.y}'),
        ('{e[0]:03d} {z[0]:03d}', '042 {z[0]:03d}'),
    ],
    ids=[
        'normal',
        'none',
        'formatting',
        'coercion',
        'formatting+coercion',
        'nesting',
        'getattr',
        'getitem',
    ]
)
def test_sformat(s, expected):
    # test a bunch of random stuff
    data = dict(
        a=1,
        b=2.0,
        c='3',
        d={'k': 'v'},
        e=[42],
    )
    assert expected == sformat(s, **data)

回答 10

我的建议如下(使用Python3.6测试):

class Lazymap(object):
       def __init__(self, **kwargs):
           self.dict = kwargs

       def __getitem__(self, key):
           return self.dict.get(key, "".join(["{", key, "}"]))


s = '{foo} {bar}'

s.format_map(Lazymap(bar="FOO"))
# >>> '{foo} FOO'

s.format_map(Lazymap(bar="BAR"))
# >>> '{foo} BAR'

s.format_map(Lazymap(bar="BAR", foo="FOO", baz="BAZ"))
# >>> 'FOO BAR'

更新:此处显示了 更优雅的方式(子类化dict和重载__missing__(self, key)):https : //stackoverflow.com/a/17215533/333403

My suggestion would be the following (tested with Python3.6):

class Lazymap(object):
       def __init__(self, **kwargs):
           self.dict = kwargs

       def __getitem__(self, key):
           return self.dict.get(key, "".join(["{", key, "}"]))


s = '{foo} {bar}'

s.format_map(Lazymap(bar="FOO"))
# >>> '{foo} FOO'

s.format_map(Lazymap(bar="BAR"))
# >>> '{foo} BAR'

s.format_map(Lazymap(bar="BAR", foo="FOO", baz="BAZ"))
# >>> 'FOO BAR'

Update: An even more elegant way (subclassing dict and overloading __missing__(self, key)) is shown here: https://stackoverflow.com/a/17215533/333403


回答 11

假设在字符串完全填写之前不使用它,您可以执行类似此类的操作:

class IncrementalFormatting:
    def __init__(self, string):
        self._args = []
        self._kwargs = {}
        self._string = string

    def add(self, *args, **kwargs):
        self._args.extend(args)
        self._kwargs.update(kwargs)

    def get(self):
        return self._string.format(*self._args, **self._kwargs)

例:

template = '#{a}:{}/{}?{c}'
message = IncrementalFormatting(template)
message.add('abc')
message.add('xyz', a=24)
message.add(c='lmno')
assert message.get() == '#24:abc/xyz?lmno'

Assuming you won’t use the string until it’s completely filled out, you could do something like this class:

class IncrementalFormatting:
    def __init__(self, string):
        self._args = []
        self._kwargs = {}
        self._string = string

    def add(self, *args, **kwargs):
        self._args.extend(args)
        self._kwargs.update(kwargs)

    def get(self):
        return self._string.format(*self._args, **self._kwargs)

Example:

template = '#{a}:{}/{}?{c}'
message = IncrementalFormatting(template)
message.add('abc')
message.add('xyz', a=24)
message.add(c='lmno')
assert message.get() == '#24:abc/xyz?lmno'

回答 12

还有另一种方法可以实现这一目标,即使用format%替换变量。例如:

>>> s = '{foo} %(bar)s'
>>> s = s.format(foo='my_foo')
>>> s
'my_foo %(bar)s'
>>> s % {'bar': 'my_bar'}
'my_foo my_bar'

There is one more way to achieve this i.e by using format and % to replace variables. For example:

>>> s = '{foo} %(bar)s'
>>> s = s.format(foo='my_foo')
>>> s
'my_foo %(bar)s'
>>> s % {'bar': 'my_bar'}
'my_foo my_bar'

回答 13

对我来说,一个非常丑陋但最简单的解决方案是:

tmpl = '{foo}, {bar}'
tmpl.replace('{bar}', 'BAR')
Out[3]: '{foo}, BAR'

这样,您仍然可以tmpl用作常规模板并仅在需要时执行部分格式化。我觉得这个问题太微不足道了,无法使用像Mohan Raj’s这样的过分解决方案。

A very ugly but the simplest solution for me is to just do:

tmpl = '{foo}, {bar}'
tmpl.replace('{bar}', 'BAR')
Out[3]: '{foo}, BAR'

This way you still can use tmpl as regular template and perform partial formatting only when needed. I find this problem too trivial to use a overkilling solution like Mohan Raj’s.


回答 14

测试从最有前途的解决方案后,在这里那里,我认识到,没有一个是满足下列要求:

  1. 严格遵守str.format_map()模板可识别的语法;
  2. 能够保留复杂的格式,即完全支持Format Mini-Language

因此,我编写了自己的解决方案,该解决方案可以满足上述要求。(编辑:现在@SvenMarnach的版本-如这个答案所报道-似乎处理了我需要的一些特殊情况)。

基本上,我最终解析了模板字符串,找到了匹配的嵌套{.*?}组(使用find_all()辅助函数),并逐步并直接使用格式化的字符串,str.format_map()同时捕捉了任何潜在的可能性KeyError

def find_all(
        text,
        pattern,
        overlap=False):
    """
    Find all occurrencies of the pattern in the text.

    Args:
        text (str|bytes|bytearray): The input text.
        pattern (str|bytes|bytearray): The pattern to find.
        overlap (bool): Detect overlapping patterns.

    Yields:
        position (int): The position of the next finding.
    """
    len_text = len(text)
    offset = 1 if overlap else (len(pattern) or 1)
    i = 0
    while i < len_text:
        i = text.find(pattern, i)
        if i >= 0:
            yield i
            i += offset
        else:
            break
def matching_delimiters(
        text,
        l_delim,
        r_delim,
        including=True):
    """
    Find matching delimiters in a sequence.

    The delimiters are matched according to nesting level.

    Args:
        text (str|bytes|bytearray): The input text.
        l_delim (str|bytes|bytearray): The left delimiter.
        r_delim (str|bytes|bytearray): The right delimiter.
        including (bool): Include delimeters.

    yields:
        result (tuple[int]): The matching delimiters.
    """
    l_offset = len(l_delim) if including else 0
    r_offset = len(r_delim) if including else 0
    stack = []

    l_tokens = set(find_all(text, l_delim))
    r_tokens = set(find_all(text, r_delim))
    positions = l_tokens.union(r_tokens)
    for pos in sorted(positions):
        if pos in l_tokens:
            stack.append(pos + 1)
        elif pos in r_tokens:
            if len(stack) > 0:
                prev = stack.pop()
                yield (prev - l_offset, pos + r_offset, len(stack))
            else:
                raise ValueError(
                    'Found `{}` unmatched right token(s) `{}` (position: {}).'
                        .format(len(r_tokens) - len(l_tokens), r_delim, pos))
    if len(stack) > 0:
        raise ValueError(
            'Found `{}` unmatched left token(s) `{}` (position: {}).'
                .format(
                len(l_tokens) - len(r_tokens), l_delim, stack.pop() - 1))
def safe_format_map(
        text,
        source):
    """
    Perform safe string formatting from a mapping source.

    If a value is missing from source, this is simply ignored, and no
    `KeyError` is raised.

    Args:
        text (str): Text to format.
        source (Mapping|None): The mapping to use as source.
            If None, uses caller's `vars()`.

    Returns:
        result (str): The formatted text.
    """
    stack = []
    for i, j, depth in matching_delimiters(text, '{', '}'):
        if depth == 0:
            try:
                replacing = text[i:j].format_map(source)
            except KeyError:
                pass
            else:
                stack.append((i, j, replacing))
    result = ''
    i, j = len(text), 0
    while len(stack) > 0:
        last_i = i
        i, j, replacing = stack.pop()
        result = replacing + text[j:last_i] + result
    if i > 0:
        result = text[0:i] + result
    return result

(此代码也可在FlyingCircus中使用 -免责声明:我是它的主要作者。)


该代码的用法是:

print(safe_format_map('{a} {b} {c}', dict(a=-A-)))
# -A- {b} {c}

让我们比较这对我最喜欢的溶液(@SvenMarnach谁亲切地分享他的代码在这里那里):

import string


class FormatPlaceholder:
    def __init__(self, key):
        self.key = key
    def __format__(self, spec):
        result = self.key
        if spec:
            result += ":" + spec
        return "{" + result + "}"
    def __getitem__(self, index):
        self.key = "{}[{}]".format(self.key, index)
        return self
    def __getattr__(self, attr):
        self.key = "{}.{}".format(self.key, attr)
        return self


class FormatDict(dict):
    def __missing__(self, key):
        return FormatPlaceholder(key)


def safe_format_alt(text, source):
    formatter = string.Formatter()
    return formatter.vformat(text, (), FormatDict(source))

以下是一些测试:

test_texts = (
    '{b} {f}',  # simple nothing useful in source
    '{a} {b}',  # simple
    '{a} {b} {c:5d}',  # formatting
    '{a} {b} {c!s}',  # coercion
    '{a} {b} {c!s:>{a}s}',  # formatting and coercion
    '{a} {b} {c:0{a}d}',  # nesting
    '{a} {b} {d[x]}',  # dicts (existing in source)
    '{a} {b} {e.index}',  # class (existing in source)
    '{a} {b} {f[g]}',  # dict (not existing in source)
    '{a} {b} {f.values}',  # class (not existing in source)

)
source = dict(a=4, c=101, d=dict(x='FOO'), e=[])

以及使其运行的代码:

funcs = safe_format_map, safe_format_alt

n = 18
for text in test_texts:
    full_source = {**dict(b='---', f=dict(g='Oh yes!')), **source}
    print('{:>{n}s} :   OK   : '.format('str.format_map', n=n) + text.format_map(full_source))
    for func in funcs:
        try:
            print(f'{func.__name__:>{n}s} :   OK   : ' + func(text, source))
        except:
            print(f'{func.__name__:>{n}s} : FAILED : {text}')

导致:

    str.format_map :   OK   : --- {'g': 'Oh yes!'}
   safe_format_map :   OK   : {b} {f}
   safe_format_alt :   OK   : {b} {f}
    str.format_map :   OK   : 4 ---
   safe_format_map :   OK   : 4 {b}
   safe_format_alt :   OK   : 4 {b}
    str.format_map :   OK   : 4 ---   101
   safe_format_map :   OK   : 4 {b}   101
   safe_format_alt :   OK   : 4 {b}   101
    str.format_map :   OK   : 4 --- 101
   safe_format_map :   OK   : 4 {b} 101
   safe_format_alt :   OK   : 4 {b} 101
    str.format_map :   OK   : 4 ---  101
   safe_format_map :   OK   : 4 {b}  101
   safe_format_alt :   OK   : 4 {b}  101
    str.format_map :   OK   : 4 --- 0101
   safe_format_map :   OK   : 4 {b} 0101
   safe_format_alt :   OK   : 4 {b} 0101
    str.format_map :   OK   : 4 --- FOO
   safe_format_map :   OK   : 4 {b} FOO
   safe_format_alt :   OK   : 4 {b} FOO
    str.format_map :   OK   : 4 --- <built-in method index of list object at 0x7f7a485666c8>
   safe_format_map :   OK   : 4 {b} <built-in method index of list object at 0x7f7a485666c8>
   safe_format_alt :   OK   : 4 {b} <built-in method index of list object at 0x7f7a485666c8>
    str.format_map :   OK   : 4 --- Oh yes!
   safe_format_map :   OK   : 4 {b} {f[g]}
   safe_format_alt :   OK   : 4 {b} {f[g]}
    str.format_map :   OK   : 4 --- <built-in method values of dict object at 0x7f7a485da090>
   safe_format_map :   OK   : 4 {b} {f.values}
   safe_format_alt :   OK   : 4 {b} {f.values}

如您所见,更新的版本现在似乎可以很好地处理早期版本曾经失败的情况。


在时间上,它们在大约范围内。彼此之间的50%取决于实际text格式化的格式(可能还有实际的格式source),但safe_format_map()在我执行的大多数测试中似乎都具有优势(当然,无论它们是什么意思):

for text in test_texts:
    print(f'  {text}')
    %timeit safe_format(text * 1000, source)
    %timeit safe_format_alt(text * 1000, source)
  {b} {f}
3.93 ms ± 153 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
6.35 ms ± 51.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b}
4.37 ms ± 57.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.2 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c:5d}
7.15 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.76 ms ± 69.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c!s}
7.04 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.56 ms ± 161 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c!s:>{a}s}
8.91 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.5 ms ± 181 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c:0{a}d}
8.84 ms ± 147 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.2 ms ± 202 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {d[x]}
7.01 ms ± 197 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.35 ms ± 106 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {e.index}
11 ms ± 68.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
8.78 ms ± 405 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {f[g]}
6.55 ms ± 88.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.12 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {f.values}
6.61 ms ± 55.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.92 ms ± 98.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

After testing the most promising solutions from here and there, I realized that none of them really met the following requirements:

  1. strictly adhere to the syntax recognized by str.format_map() for the template;
  2. being able to retain complex formatting, i.e. fully supporting the Format Mini-Language

So, I wrote my own solution, which satisfies the above requirements. (EDIT: now the version by @SvenMarnach — as reported in this answer — seems to handle the corner cases I needed).

Basically, I ended up parsing the template string, finding matching nested {.*?} groups (using a find_all() helper function) and building the formatted string progressively and directly using str.format_map() while catching any potential KeyError.

def find_all(
        text,
        pattern,
        overlap=False):
    """
    Find all occurrencies of the pattern in the text.

    Args:
        text (str|bytes|bytearray): The input text.
        pattern (str|bytes|bytearray): The pattern to find.
        overlap (bool): Detect overlapping patterns.

    Yields:
        position (int): The position of the next finding.
    """
    len_text = len(text)
    offset = 1 if overlap else (len(pattern) or 1)
    i = 0
    while i < len_text:
        i = text.find(pattern, i)
        if i >= 0:
            yield i
            i += offset
        else:
            break
def matching_delimiters(
        text,
        l_delim,
        r_delim,
        including=True):
    """
    Find matching delimiters in a sequence.

    The delimiters are matched according to nesting level.

    Args:
        text (str|bytes|bytearray): The input text.
        l_delim (str|bytes|bytearray): The left delimiter.
        r_delim (str|bytes|bytearray): The right delimiter.
        including (bool): Include delimeters.

    yields:
        result (tuple[int]): The matching delimiters.
    """
    l_offset = len(l_delim) if including else 0
    r_offset = len(r_delim) if including else 0
    stack = []

    l_tokens = set(find_all(text, l_delim))
    r_tokens = set(find_all(text, r_delim))
    positions = l_tokens.union(r_tokens)
    for pos in sorted(positions):
        if pos in l_tokens:
            stack.append(pos + 1)
        elif pos in r_tokens:
            if len(stack) > 0:
                prev = stack.pop()
                yield (prev - l_offset, pos + r_offset, len(stack))
            else:
                raise ValueError(
                    'Found `{}` unmatched right token(s) `{}` (position: {}).'
                        .format(len(r_tokens) - len(l_tokens), r_delim, pos))
    if len(stack) > 0:
        raise ValueError(
            'Found `{}` unmatched left token(s) `{}` (position: {}).'
                .format(
                len(l_tokens) - len(r_tokens), l_delim, stack.pop() - 1))
def safe_format_map(
        text,
        source):
    """
    Perform safe string formatting from a mapping source.

    If a value is missing from source, this is simply ignored, and no
    `KeyError` is raised.

    Args:
        text (str): Text to format.
        source (Mapping|None): The mapping to use as source.
            If None, uses caller's `vars()`.

    Returns:
        result (str): The formatted text.
    """
    stack = []
    for i, j, depth in matching_delimiters(text, '{', '}'):
        if depth == 0:
            try:
                replacing = text[i:j].format_map(source)
            except KeyError:
                pass
            else:
                stack.append((i, j, replacing))
    result = ''
    i, j = len(text), 0
    while len(stack) > 0:
        last_i = i
        i, j, replacing = stack.pop()
        result = replacing + text[j:last_i] + result
    if i > 0:
        result = text[0:i] + result
    return result

(This code is also available in FlyingCircus — DISCLAIMER: I am the main author of it.)


The usage for this code would be:

print(safe_format_map('{a} {b} {c}', dict(a=-A-)))
# -A- {b} {c}

Let’s compare this to the my favourite solution (by @SvenMarnach who kindly shared his code here and there):

import string


class FormatPlaceholder:
    def __init__(self, key):
        self.key = key
    def __format__(self, spec):
        result = self.key
        if spec:
            result += ":" + spec
        return "{" + result + "}"
    def __getitem__(self, index):
        self.key = "{}[{}]".format(self.key, index)
        return self
    def __getattr__(self, attr):
        self.key = "{}.{}".format(self.key, attr)
        return self


class FormatDict(dict):
    def __missing__(self, key):
        return FormatPlaceholder(key)


def safe_format_alt(text, source):
    formatter = string.Formatter()
    return formatter.vformat(text, (), FormatDict(source))

Here are a couple of tests:

test_texts = (
    '{b} {f}',  # simple nothing useful in source
    '{a} {b}',  # simple
    '{a} {b} {c:5d}',  # formatting
    '{a} {b} {c!s}',  # coercion
    '{a} {b} {c!s:>{a}s}',  # formatting and coercion
    '{a} {b} {c:0{a}d}',  # nesting
    '{a} {b} {d[x]}',  # dicts (existing in source)
    '{a} {b} {e.index}',  # class (existing in source)
    '{a} {b} {f[g]}',  # dict (not existing in source)
    '{a} {b} {f.values}',  # class (not existing in source)

)
source = dict(a=4, c=101, d=dict(x='FOO'), e=[])

and the code to make it running:

funcs = safe_format_map, safe_format_alt

n = 18
for text in test_texts:
    full_source = {**dict(b='---', f=dict(g='Oh yes!')), **source}
    print('{:>{n}s} :   OK   : '.format('str.format_map', n=n) + text.format_map(full_source))
    for func in funcs:
        try:
            print(f'{func.__name__:>{n}s} :   OK   : ' + func(text, source))
        except:
            print(f'{func.__name__:>{n}s} : FAILED : {text}')

resulting in:

    str.format_map :   OK   : --- {'g': 'Oh yes!'}
   safe_format_map :   OK   : {b} {f}
   safe_format_alt :   OK   : {b} {f}
    str.format_map :   OK   : 4 ---
   safe_format_map :   OK   : 4 {b}
   safe_format_alt :   OK   : 4 {b}
    str.format_map :   OK   : 4 ---   101
   safe_format_map :   OK   : 4 {b}   101
   safe_format_alt :   OK   : 4 {b}   101
    str.format_map :   OK   : 4 --- 101
   safe_format_map :   OK   : 4 {b} 101
   safe_format_alt :   OK   : 4 {b} 101
    str.format_map :   OK   : 4 ---  101
   safe_format_map :   OK   : 4 {b}  101
   safe_format_alt :   OK   : 4 {b}  101
    str.format_map :   OK   : 4 --- 0101
   safe_format_map :   OK   : 4 {b} 0101
   safe_format_alt :   OK   : 4 {b} 0101
    str.format_map :   OK   : 4 --- FOO
   safe_format_map :   OK   : 4 {b} FOO
   safe_format_alt :   OK   : 4 {b} FOO
    str.format_map :   OK   : 4 --- <built-in method index of list object at 0x7f7a485666c8>
   safe_format_map :   OK   : 4 {b} <built-in method index of list object at 0x7f7a485666c8>
   safe_format_alt :   OK   : 4 {b} <built-in method index of list object at 0x7f7a485666c8>
    str.format_map :   OK   : 4 --- Oh yes!
   safe_format_map :   OK   : 4 {b} {f[g]}
   safe_format_alt :   OK   : 4 {b} {f[g]}
    str.format_map :   OK   : 4 --- <built-in method values of dict object at 0x7f7a485da090>
   safe_format_map :   OK   : 4 {b} {f.values}
   safe_format_alt :   OK   : 4 {b} {f.values}

as you can see, the updated version now seems to handle well the corner cases where the earlier version used to fail.


Timewise, they are within approx. 50% of each other, depending on the actual text to format (and likely the actual source), but safe_format_map() seems to have an edge in most of the tests I performed (whatever they mean, of course):

for text in test_texts:
    print(f'  {text}')
    %timeit safe_format(text * 1000, source)
    %timeit safe_format_alt(text * 1000, source)
  {b} {f}
3.93 ms ± 153 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
6.35 ms ± 51.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b}
4.37 ms ± 57.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.2 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c:5d}
7.15 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.76 ms ± 69.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c!s}
7.04 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.56 ms ± 161 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c!s:>{a}s}
8.91 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.5 ms ± 181 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {c:0{a}d}
8.84 ms ± 147 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.2 ms ± 202 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {d[x]}
7.01 ms ± 197 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.35 ms ± 106 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {e.index}
11 ms ± 68.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
8.78 ms ± 405 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {f[g]}
6.55 ms ± 88.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.12 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  {a} {b} {f.values}
6.61 ms ± 55.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.92 ms ± 98.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

回答 15

如果您想解开字典以将参数传递给format如本相关问题所示,则可以使用以下方法。

首先假设字符串s与此问题相同:

s = '{foo} {bar}'

值由以下字典给出:

replacements = {'foo': 'FOO'}

显然这是行不通的:

s.format(**replacements)
#---------------------------------------------------------------------------
#KeyError                                  Traceback (most recent call last)
#<ipython-input-29-ef5e51de79bf> in <module>()
#----> 1 s.format(**replacements)
#
#KeyError: 'bar'

但是,您可以首先从中获取set所有命名参数,s然后创建一个字典,将参数映射到用大括号括起来的自身:

from string import Formatter
args = {x[1]:'{'+x[1]+'}' for x in Formatter().parse(s)}
print(args)
#{'foo': '{foo}', 'bar': '{bar}'}

现在,使用args字典来填写中缺少的键replacements。对于python 3.5+,您可以在单个表达式中执行此操作

new_s = s.format(**{**args, **replacements}}
print(new_s)
#'FOO {bar}'

对于旧版本的python,您可以调用update

args.update(replacements)
print(s.format(**args))
#'FOO {bar}'

If you’d like to unpack a dictionary to pass arguments to format, as in this related question, you could use the following method.

First assume the string s is the same as in this question:

s = '{foo} {bar}'

and the values are given by the following dictionary:

replacements = {'foo': 'FOO'}

Clearly this won’t work:

s.format(**replacements)
#---------------------------------------------------------------------------
#KeyError                                  Traceback (most recent call last)
#<ipython-input-29-ef5e51de79bf> in <module>()
#----> 1 s.format(**replacements)
#
#KeyError: 'bar'

However, you could first get a set of all of the named arguments from s and create a dictionary that maps the argument to itself wrapped in curly braces:

from string import Formatter
args = {x[1]:'{'+x[1]+'}' for x in Formatter().parse(s)}
print(args)
#{'foo': '{foo}', 'bar': '{bar}'}

Now use the args dictionary to fill in the missing keys in replacements. For python 3.5+, you can do this in a single expression:

new_s = s.format(**{**args, **replacements}}
print(new_s)
#'FOO {bar}'

For older versions of python, you could call update:

args.update(replacements)
print(s.format(**args))
#'FOO {bar}'

回答 16

我喜欢@ sven-marnach的答案。我的答案只是它的扩展版本。它允许非关键字格式,并忽略多余的键。以下是用法示例(函数名称是对python 3.6 f字符串格式的引用):

# partial string substitution by keyword
>>> f('{foo} {bar}', foo="FOO")
'FOO {bar}'

# partial string substitution by argument
>>> f('{} {bar}', 1)
'1 {bar}'

>>> f('{foo} {}', 1)
'{foo} 1'

# partial string substitution with arguments and keyword mixed
>>> f('{foo} {} {bar} {}', '|', bar='BAR')
'{foo} | BAR {}'

# partial string substitution with extra keyword
>>> f('{foo} {bar}', foo="FOO", bro="BRO")
'FOO {bar}'

# you can simply 'pour out' your dictionary to format function
>>> kwargs = {'foo': 'FOO', 'bro': 'BRO'}
>>> f('{foo} {bar}', **kwargs)
'FOO {bar}'

这是我的代码:

from string import Formatter


class FormatTuple(tuple):
    def __getitem__(self, key):
        if key + 1 > len(self):
            return "{}"
        return tuple.__getitem__(self, key)


class FormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"


def f(string, *args, **kwargs):
    """
    String safe substitute format method.
    If you pass extra keys they will be ignored.
    If you pass incomplete substitute map, missing keys will be left unchanged.
    :param string:
    :param kwargs:
    :return:

    >>> f('{foo} {bar}', foo="FOO")
    'FOO {bar}'
    >>> f('{} {bar}', 1)
    '1 {bar}'
    >>> f('{foo} {}', 1)
    '{foo} 1'
    >>> f('{foo} {} {bar} {}', '|', bar='BAR')
    '{foo} | BAR {}'
    >>> f('{foo} {bar}', foo="FOO", bro="BRO")
    'FOO {bar}'
    """
    formatter = Formatter()
    args_mapping = FormatTuple(args)
    mapping = FormatDict(kwargs)
    return formatter.vformat(string, args_mapping, mapping)

I like @sven-marnach answer. My answer is simply an extended version of it. It allows non-keyword formatting and ignores extra keys. Here are examples of usage (the name of a function is a reference to python 3.6 f-string formatting):

# partial string substitution by keyword
>>> f('{foo} {bar}', foo="FOO")
'FOO {bar}'

# partial string substitution by argument
>>> f('{} {bar}', 1)
'1 {bar}'

>>> f('{foo} {}', 1)
'{foo} 1'

# partial string substitution with arguments and keyword mixed
>>> f('{foo} {} {bar} {}', '|', bar='BAR')
'{foo} | BAR {}'

# partial string substitution with extra keyword
>>> f('{foo} {bar}', foo="FOO", bro="BRO")
'FOO {bar}'

# you can simply 'pour out' your dictionary to format function
>>> kwargs = {'foo': 'FOO', 'bro': 'BRO'}
>>> f('{foo} {bar}', **kwargs)
'FOO {bar}'

And here is my code:

from string import Formatter


class FormatTuple(tuple):
    def __getitem__(self, key):
        if key + 1 > len(self):
            return "{}"
        return tuple.__getitem__(self, key)


class FormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"


def f(string, *args, **kwargs):
    """
    String safe substitute format method.
    If you pass extra keys they will be ignored.
    If you pass incomplete substitute map, missing keys will be left unchanged.
    :param string:
    :param kwargs:
    :return:

    >>> f('{foo} {bar}', foo="FOO")
    'FOO {bar}'
    >>> f('{} {bar}', 1)
    '1 {bar}'
    >>> f('{foo} {}', 1)
    '{foo} 1'
    >>> f('{foo} {} {bar} {}', '|', bar='BAR')
    '{foo} | BAR {}'
    >>> f('{foo} {bar}', foo="FOO", bro="BRO")
    'FOO {bar}'
    """
    formatter = Formatter()
    args_mapping = FormatTuple(args)
    mapping = FormatDict(kwargs)
    return formatter.vformat(string, args_mapping, mapping)

回答 17

如果您进行了大量的模板化工作,并且发现Python的内置字符串模板化功能不足或笨拙,请查看Jinja2

从文档:

Jinja是一种以Django模板为模型的,现代且设计友好的Python模板语言。

If you’re doing a lot of templating and finding Python’s built in string templating functionality to be insufficient or clunky, look at Jinja2.

From the docs:

Jinja is a modern and designer-friendly templating language for Python, modelled after Django’s templates.


回答 18

阅读@Sam Bourne的注释,我修改了@SvenMarnach的代码 以正确地使用强制(如{a!s:>2s}),而无需编写自定义解析器。基本思想不是转换为字符串,而是将带有强制标签的缺失键连接起来。

import string
class MissingKey(object):
    def __init__(self, key):
        self.key = key

    def __str__(self):  # Supports {key!s}
        return MissingKeyStr("".join([self.key, "!s"]))

    def __repr__(self):  # Supports {key!r}
        return MissingKeyStr("".join([self.key, "!r"]))

    def __format__(self, spec): # Supports {key:spec}
        if spec:
            return "".join(["{", self.key, ":", spec, "}"])
        return "".join(["{", self.key, "}"])

    def __getitem__(self, i): # Supports {key[i]}
        return MissingKey("".join([self.key, "[", str(i), "]"]))

    def __getattr__(self, name): # Supports {key.name}
        return MissingKey("".join([self.key, ".", name]))


class MissingKeyStr(MissingKey, str):
    def __init__(self, key):
        if isinstance(key, MissingKey):
            self.key = "".join([key.key, "!s"])
        else:
            self.key = key

class SafeFormatter(string.Formatter):
    def __init__(self, default=lambda k: MissingKey(k)):
        self.default=default

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            return kwds.get(key, self.default(key))
        else:
            return super().get_value(key, args, kwds)

像这样使用(例如)

SafeFormatter().format("{a:<5} {b:<10}", a=10)

以下测试(受@ norok2测试的启发)在两种情况下检查传统类format_map和a safe_format_map类的输出:提供正确的关键字或不提供关键字。

def safe_format_map(text, source):
    return SafeFormatter().format(text, **source)

test_texts = (
    '{a} ',             # simple nothing useful in source
    '{a:5d}',       # formatting
    '{a!s}',        # coercion
    '{a!s:>{a}s}',  # formatting and coercion
    '{a:0{a}d}',    # nesting
    '{d[x]}',       # indexing
    '{d.values}',   # member
)

source = dict(a=10,d=dict(x='FOO'))
funcs = [safe_format_map,
         str.format_map
         #safe_format_alt  # Version based on parsing (See @norok2)
         ]
n = 18
for text in test_texts:
    # full_source = {**dict(b='---', f=dict(g='Oh yes!')), **source}
    # print('{:>{n}s} :   OK   : '.format('str.format_map', n=n) + text.format_map(full_source))
    print("Testing:", text)
    for func in funcs:
        try:
            print(f'{func.__name__:>{n}s} : OK\t\t\t: ' + func(text, dict()))
        except:
            print(f'{func.__name__:>{n}s} : FAILED')

        try:
            print(f'{func.__name__:>{n}s} : OK\t\t\t: ' + func(text, source))
        except:
            print(f'{func.__name__:>{n}s} : FAILED')

哪些输出

Testing: {a} 
   safe_format_map : OK         : {a} 
   safe_format_map : OK         : 10 
        format_map : FAILED
        format_map : OK         : 10 
Testing: {a:5d}
   safe_format_map : OK         : {a:5d}
   safe_format_map : OK         :    10
        format_map : FAILED
        format_map : OK         :    10
Testing: {a!s}
   safe_format_map : OK         : {a!s}
   safe_format_map : OK         : 10
        format_map : FAILED
        format_map : OK         : 10
Testing: {a!s:>{a}s}
   safe_format_map : OK         : {a!s:>{a}s}
   safe_format_map : OK         :         10
        format_map : FAILED
        format_map : OK         :         10
Testing: {a:0{a}d}
   safe_format_map : OK         : {a:0{a}d}
   safe_format_map : OK         : 0000000010
        format_map : FAILED
        format_map : OK         : 0000000010
Testing: {d[x]}
   safe_format_map : OK         : {d[x]}
   safe_format_map : OK         : FOO
        format_map : FAILED
        format_map : OK         : FOO
Testing: {d.values}
   safe_format_map : OK         : {d.values}
   safe_format_map : OK         : <built-in method values of dict object at 0x7fe61e230af8>
        format_map : FAILED
        format_map : OK         : <built-in method values of dict object at 0x7fe61e230af8>

Reading @Sam Bourne comment, I modified @SvenMarnach’s code to work properly with coercion (like {a!s:>2s}) without writing a custom parser. The basic idea is not to convert to strings but concatenate missing keys with coercion tags.

import string
class MissingKey(object):
    def __init__(self, key):
        self.key = key

    def __str__(self):  # Supports {key!s}
        return MissingKeyStr("".join([self.key, "!s"]))

    def __repr__(self):  # Supports {key!r}
        return MissingKeyStr("".join([self.key, "!r"]))

    def __format__(self, spec): # Supports {key:spec}
        if spec:
            return "".join(["{", self.key, ":", spec, "}"])
        return "".join(["{", self.key, "}"])

    def __getitem__(self, i): # Supports {key[i]}
        return MissingKey("".join([self.key, "[", str(i), "]"]))

    def __getattr__(self, name): # Supports {key.name}
        return MissingKey("".join([self.key, ".", name]))


class MissingKeyStr(MissingKey, str):
    def __init__(self, key):
        if isinstance(key, MissingKey):
            self.key = "".join([key.key, "!s"])
        else:
            self.key = key

class SafeFormatter(string.Formatter):
    def __init__(self, default=lambda k: MissingKey(k)):
        self.default=default

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            return kwds.get(key, self.default(key))
        else:
            return super().get_value(key, args, kwds)

Use (for example) like this

SafeFormatter().format("{a:<5} {b:<10}", a=10)

The following tests (inspired by tests from @norok2) check the output for the traditional format_map and a safe_format_map based on the class above in two cases: providing correct keywords or without them.

def safe_format_map(text, source):
    return SafeFormatter().format(text, **source)

test_texts = (
    '{a} ',             # simple nothing useful in source
    '{a:5d}',       # formatting
    '{a!s}',        # coercion
    '{a!s:>{a}s}',  # formatting and coercion
    '{a:0{a}d}',    # nesting
    '{d[x]}',       # indexing
    '{d.values}',   # member
)

source = dict(a=10,d=dict(x='FOO'))
funcs = [safe_format_map,
         str.format_map
         #safe_format_alt  # Version based on parsing (See @norok2)
         ]
n = 18
for text in test_texts:
    # full_source = {**dict(b='---', f=dict(g='Oh yes!')), **source}
    # print('{:>{n}s} :   OK   : '.format('str.format_map', n=n) + text.format_map(full_source))
    print("Testing:", text)
    for func in funcs:
        try:
            print(f'{func.__name__:>{n}s} : OK\t\t\t: ' + func(text, dict()))
        except:
            print(f'{func.__name__:>{n}s} : FAILED')

        try:
            print(f'{func.__name__:>{n}s} : OK\t\t\t: ' + func(text, source))
        except:
            print(f'{func.__name__:>{n}s} : FAILED')

Which outputs

Testing: {a} 
   safe_format_map : OK         : {a} 
   safe_format_map : OK         : 10 
        format_map : FAILED
        format_map : OK         : 10 
Testing: {a:5d}
   safe_format_map : OK         : {a:5d}
   safe_format_map : OK         :    10
        format_map : FAILED
        format_map : OK         :    10
Testing: {a!s}
   safe_format_map : OK         : {a!s}
   safe_format_map : OK         : 10
        format_map : FAILED
        format_map : OK         : 10
Testing: {a!s:>{a}s}
   safe_format_map : OK         : {a!s:>{a}s}
   safe_format_map : OK         :         10
        format_map : FAILED
        format_map : OK         :         10
Testing: {a:0{a}d}
   safe_format_map : OK         : {a:0{a}d}
   safe_format_map : OK         : 0000000010
        format_map : FAILED
        format_map : OK         : 0000000010
Testing: {d[x]}
   safe_format_map : OK         : {d[x]}
   safe_format_map : OK         : FOO
        format_map : FAILED
        format_map : OK         : FOO
Testing: {d.values}
   safe_format_map : OK         : {d.values}
   safe_format_map : OK         : <built-in method values of dict object at 0x7fe61e230af8>
        format_map : FAILED
        format_map : OK         : <built-in method values of dict object at 0x7fe61e230af8>

回答 19

您可以将其包装在带有默认参数的函数中:

def print_foo_bar(foo='', bar=''):
    s = '{foo} {bar}'
    return s.format(foo=foo, bar=bar)

print_foo_bar(bar='BAR') # ' BAR'

You could wrap it in a function that takes default arguments:

def print_foo_bar(foo='', bar=''):
    s = '{foo} {bar}'
    return s.format(foo=foo, bar=bar)

print_foo_bar(bar='BAR') # ' BAR'

删除一长串文本中的所有换行符

问题:删除一长串文本中的所有换行符

基本上,我是在要求用户在控制台中输入文本字符串,但是该字符串很长,并且包含许多换行符。我将如何获取用户的字符串并删除所有换行符以使其成为一行文本。我获取字符串的方法非常简单。

string = raw_input("Please enter string: ")

我应该从用户那里获取字符串吗?我在Mac上运行Python 2.7.4。

PS显然,我是一个菜鸟,所以即使解决方案不是最有效的,也应感谢使用最简单语法的解决方案。

Basically, I’m asking the user to input a string of text into the console, but the string is very long and includes many line breaks. How would I take the user’s string and delete all line breaks to make it a single line of text. My method for acquiring the string is very simple.

string = raw_input("Please enter string: ")

Is there a different way I should be grabbing the string from the user? I’m running Python 2.7.4 on a Mac.

P.S. Clearly I’m a noob, so even if a solution isn’t the most efficient, the one that uses the most simple syntax would be appreciated.


回答 0

您如何输入换行符raw_input?但是,一旦您的字符串中包含一些字符,就想摆脱replace它们。

>>> mystr = raw_input('please enter string: ')
please enter string: hello world, how do i enter line breaks?
>>> # pressing enter didn't work...
...
>>> mystr
'hello world, how do i enter line breaks?'
>>> mystr.replace(' ', '')
'helloworld,howdoienterlinebreaks?'
>>>

在上面的示例中,我替换了所有空格。该字符串'\n'表示换行符。并\r代表回车(如果您在Windows上,则可能会得到这些,一秒钟replace就会为您处理!)。

基本上:

# you probably want to use a space ' ' to replace `\n`
mystring = mystring.replace('\n', ' ').replace('\r', '')

还要注意,调用变量是一个坏主意string,因为这会遮盖模块string。我会避免使用但会在某些时候使用的另一个名称:file。为了同样的原因。

How do you enter line breaks with raw_input? But, once you have a string with some characters in it you want to get rid of, just replace them.

>>> mystr = raw_input('please enter string: ')
please enter string: hello world, how do i enter line breaks?
>>> # pressing enter didn't work...
...
>>> mystr
'hello world, how do i enter line breaks?'
>>> mystr.replace(' ', '')
'helloworld,howdoienterlinebreaks?'
>>>

In the example above, I replaced all spaces. The string '\n' represents newlines. And \r represents carriage returns (if you’re on windows, you might be getting these and a second replace will handle them for you!).

basically:

# you probably want to use a space ' ' to replace `\n`
mystring = mystring.replace('\n', ' ').replace('\r', '')

Note also, that it is a bad idea to call your variable string, as this shadows the module string. Another name I’d avoid but would love to use sometimes: file. For the same reason.


回答 1

您可以尝试使用字符串替换:

string = string.replace('\r', '').replace('\n', '')

You can try using string replace:

string = string.replace('\r', '').replace('\n', '')

回答 2

您可以在不使用分隔符arg的情况下拆分字符串,这会将连续的空格视为单个分隔符(包括换行符和制表符)。然后使用空格加入:

In : " ".join("\n\nsome    text \r\n with multiple whitespace".split())
Out: 'some text with multiple whitespace'

https://docs.python.org/2/library/stdtypes.html#str.split

You can split the string with no separator arg, which will treat consecutive whitespace as a single separator (including newlines and tabs). Then join using a space:

In : " ".join("\n\nsome    text \r\n with multiple whitespace".split())
Out: 'some text with multiple whitespace'

https://docs.python.org/2/library/stdtypes.html#str.split


回答 3

根据Xbello评论更新:

string = my_string.rstrip('\r\n')

在这里阅读更多

updated based on Xbello comment:

string = my_string.rstrip('\r\n')

read more here


回答 4

另一个选择是正则表达式:

>>> import re
>>> re.sub("\n|\r", "", "Foo\n\rbar\n\rbaz\n\r")
'Foobarbaz'

Another option is regex:

>>> import re
>>> re.sub("\n|\r", "", "Foo\n\rbar\n\rbaz\n\r")
'Foobarbaz'

回答 5

一种考虑方法

  • 字符串开头/结尾的其他白色字符
  • 每行开头/结尾处的其他白色字符
  • 各种结束符

它需要这样的多行字符串,可能会很杂乱,例如

test_str = '\nhej ho \n aaa\r\n   a\n '

并产生漂亮的单行字符串

>>> ' '.join([line.strip() for line in test_str.strip().splitlines()])
'hej ho aaa a'

更新:要修复产生多余空格的多个换行符:

' '.join([line.strip() for line in test_str.strip().splitlines() if line.strip()])

这也适用于以下情况 test_str = '\nhej ho \n aaa\r\n\n\n\n\n a\n '

A method taking into consideration

  • additional white characters at the beginning/end of string
  • additional white characters at the beginning/end of every line
  • various end-line characters

it takes such a multi-line string which may be messy e.g.

test_str = '\nhej ho \n aaa\r\n   a\n '

and produces nice one-line string

>>> ' '.join([line.strip() for line in test_str.strip().splitlines()])
'hej ho aaa a'

UPDATE: To fix multiple new-line character producing redundant spaces:

' '.join([line.strip() for line in test_str.strip().splitlines() if line.strip()])

This works for the following too test_str = '\nhej ho \n aaa\r\n\n\n\n\n a\n '


回答 6

如果有人决定使用replace,则应r'\n'改用'\n'

mystring = mystring.replace(r'\n', ' ').replace(r'\r', '')

If anybody decides to use replace, you should try r'\n' instead '\n'

mystring = mystring.replace(r'\n', ' ').replace(r'\r', '')

回答 7

rstrip的问题在于它不能在所有情况下都起作用(正如我本人所见的那样)。相反,您可以使用-text = text.replace(“ \ n”,“”),这将删除所有带有空格的新行\ n。

预先感谢你们的支持。

The problem with rstrip is that it does not work in all cases (as I myself have seen few). Instead you can use – text= text.replace(“\n”,” “) this will remove all new line \n with a space.

Thanks in advance guys for your upvotes.


暂时禁用单个Python单元测试

问题:暂时禁用单个Python单元测试

unittest在Python中使用模块时,如何暂时禁用各个单元测试?

How can individual unit tests be temporarily disabled when using the unittest module in Python?


回答 0

单个测试方法或类都可以使用unittest.skip装饰器禁用。

@unittest.skip("reason for skipping")
def test_foo():
    print('This is foo test case.')


@unittest.skip  # no reason needed
def test_bar():
    print('This is bar test case.')

有关其他选项,请参阅文档“ 跳过测试和预期的失败”

Individual test methods or classes can both be disabled using the unittest.skip decorator.

@unittest.skip("reason for skipping")
def test_foo():
    print('This is foo test case.')


@unittest.skip  # no reason needed
def test_bar():
    print('This is bar test case.')

For other options, see the docs for Skipping tests and expected failures.


回答 1

您可以使用装饰器禁用可以包装功能的测试,并阻止googletest或python单元测试运行测试用例。

def disabled(f):
    def _decorator():
        print f.__name__ + ' has been disabled'
    return _decorator

@disabled
def testFoo():
    '''Foo test case'''
    print 'this is foo test case'

testFoo()

输出:

testFoo has been disabled

You can use decorators to disable the test that can wrap the function and prevent the googletest or python unit test to run the testcase.

def disabled(f):
    def _decorator():
        print f.__name__ + ' has been disabled'
    return _decorator

@disabled
def testFoo():
    '''Foo test case'''
    print 'this is foo test case'

testFoo()

Output:

testFoo has been disabled

回答 2

最新版本(2.7版)支持测试跳过/禁用,就像这样。您可以仅获取此模块并将其用于现有的Python安装中。它可能会工作。

在此之前,我曾经重命名过xtest_testname要从中跳过的测试test_testname


这是一个快速的elisp脚本来执行此操作。我的elisp有点生锈,因此对于出现的任何问题我都表示歉意。未经测试。

  (defun disable_enable_test ()
  (interactive "")
  (save-excursion
    (beginning-of-line)
    (search-forward "def")
    (forward-char)
    (if (looking-at "disable_")
    (zap-to-char 1 ?_)
      (insert "disable_"))))

The latest version (2.7 – unreleased) supports test skipping/disabling like so. You could just get this module and use it on your existing Python install. It will probably work.

Before this, I used to rename the tests I wanted skipped to xtest_testname from test_testname.


Here’s a quick elisp script to do this. My elisp is a little rusty so I apologise in advance for any problems it has. Untested.

  (defun disable_enable_test ()
  (interactive "")
  (save-excursion
    (beginning-of-line)
    (search-forward "def")
    (forward-char)
    (if (looking-at "disable_")
    (zap-to-char 1 ?_)
      (insert "disable_"))))

回答 3

只需将@unittest.SkipTest装饰器放在测试之上就足够了。

Simply placing @unittest.SkipTest decorator above the test is enough.


回答 4

我只是用下划线将测试用例方法重命名:test_myfunc变成_test_myfunc。

I just rename a test case method with an underscore: test_myfunc becomes _test_myfunc.


回答 5

2.1 的文档未指定忽略或跳过方法。

不过通常,我会在需要时阻止评论。

The docs for 2.1 don’t specify an ignore or skip method.

Usually though, I block comment when needed.


回答 6

专注于问题的“暂时禁用”部分,最佳答案在某种程度上取决于用例。这里带给我的用例是我正在对功能进行测试驱动的开发。在此过程中,我连续编写测试,并经常在函数中使用断点进行调试。如果我每次运行测试运行程序时都运行所有测试,那么最终我会在已经生效的测试的断点处停止。我不需要添加“跳过”或修改测试名称或类似名称,因为当我编写完函数后,我希望所有测试都可以运行。如果使用“跳过”,则必须返回并“跳过”。

对于我的用例,解决方案在于测试运行器,而不是测试代码。我用pytest。使用pytest,可以从命令行轻松指定一个测试:

pytest PYTHON_FILENAME.TEST_CLASS.TEST_NAME

(将大写替换为您的值)。

我了解该问题是针对python-unitest的。我已经很久没有使用它了。如果它与pytest类似,我不会感到惊讶。如果没有,您可以轻松切换到pytest。您无需修改​​代码。只需安装它并更改您的测试运行器命令。

另外,我使用PyCharm Pro。在显示我的测试代码的页面上,每个测试的def旁边都有一个小图标。我可以单击该图标并单独运行该测试。

Focusing on the “temporarily disabled” part of the question, the best answer somewhat depends on the use case. The use case that brought me here is I am doing test driven development on a function. In this process, I successively write tests and often use break points in the function for debugging. If I just run all the tests every time I run the test runner, I end up stopping at break points for tests that already work. Adding “skip” or munging the test name or something like that is not what I want because when I am done writing the function, I want all tests to run. If I used “skip” I would have to go back and “unskip”.

For my use case, the solution lies in the test runner, not in the test code. I use pytest. With pytest, it is easy to specify a single test from the command line:

pytest PYTHON_FILENAME.TEST_CLASS.TEST_NAME

(replace the caps with your values).

I understand the that question was for python-unitest. I have not used that in a long time. I would not be surprised if it had something similar to pytest. If not, you can easily switch to pytest. You do not need to modify your code. Just install it and change your test runner command.

Also, I use PyCharm Pro. On the page that shows my test code, there is a small icon next to the def for each test. I can click that icon and run that test individually.


读取csv时删除熊猫中的索引列

问题:读取csv时删除熊猫中的索引列

我有以下代码导入CSV文件。有3列,我想将其中的前两个设置为变量。当我将第二列设置为变量“效率”时,索引列也会被添加。如何摆脱索引列?

df = pd.DataFrame.from_csv('Efficiency_Data.csv', header=0, parse_dates=False)
energy = df.index
efficiency = df.Efficiency
print efficiency

我尝试使用

del df['index']

我设置好之后

energy = df.index

我在另一篇文章中找到的,但结果为“ KeyError:’index’”

I have the following code which imports a CSV file. There are 3 columns and I want to set the first two of them to variables. When I set the second column to the variable “efficiency” the index column is also tacked on. How can I get rid of the index column?

df = pd.DataFrame.from_csv('Efficiency_Data.csv', header=0, parse_dates=False)
energy = df.index
efficiency = df.Efficiency
print efficiency

I tried using

del df['index']

after I set

energy = df.index

which I found in another post but that results in “KeyError: ‘index’ “


回答 0

DataFrameSeries始终具有索引。尽管它显示在列旁边,但它不是列,这就是为什么它del df['index']不起作用的原因。

如果要用简单的序号替换索引,请使用df.reset_index()

要了解为什么存在索引以及如何使用该索引,请参阅距熊猫10分钟的信息

DataFrames and Series always have an index. Although it displays alongside the column(s), it is not a column, which is why del df['index'] did not work.

If you want to replace the index with simple sequential numbers, use df.reset_index().

To get a sense for why the index is there and how it is used, see e.g. 10 minutes to Pandas.


回答 1

在读取和读取CSV文件时,请包含参数index=False,例如:

 df.to_csv(filename, index=False)

并从CSV读取

df.read_csv(filename, index=False)  

这样可以防止出现此问题,因此您以后无需修复它。

When reading to and from your CSV file include the argument index=False so for example:

 df.to_csv(filename, index=False)

and to read from the csv

df.read_csv(filename, index=False)  

This should prevent the issue so you don’t need to fix it later.


回答 2

df.reset_index(drop=True, inplace=True)

df.reset_index(drop=True, inplace=True)


回答 3

您可以将其中一列设置为索引,以防万一它是“ id”。在这种情况下,索引列将替换为您选择的列之一。

df.set_index('id', inplace=True)

You can set one of the columns as an index in case it is an “id” for example. In this case the index column will be replaced by one of the columns you have chosen.

df.set_index('id', inplace=True)

回答 4

如果您的问题与我的问题相同,则只想将列标题从0重置为列大小。做

df = pd.DataFrame(df.values);

编辑:

如果您具有异构数据类型,则不是一个好主意。更好地使用

df.columns = range(len(df.columns))

If your problem is same as mine where you just want to reset the column headers from 0 to column size. Do

df = pd.DataFrame(df.values);

EDIT:

Not a good idea if you have heterogenous data types. Better just use

df.columns = range(len(df.columns))

回答 5

您可以使用from_csv函数的index_col参数在csv文件中指定哪一列是索引,如果这样做不能解决问题,请提供数据示例

you can specify which column is an index in your csv file by using index_col parameter of from_csv function if this doesn’t solve you problem please provide example of your data


回答 6

一两件事,我做的是df=df.reset_index() 那么df=df.drop(['index'],axis=1)

One thing that i do is df=df.reset_index() then df=df.drop(['index'],axis=1)


Python如何写入二进制文件?

问题:Python如何写入二进制文件?

我有一个字节列表作为整数,这就像

[120, 3, 255, 0, 100]

如何将该列表作为二进制文件写入文件?

这行得通吗?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)

I have a list of bytes as integers, which is something like

[120, 3, 255, 0, 100]

How can I write this list to a file as binary?

Would this work?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)

回答 0

这正是bytearray用于:

newFileByteArray = bytearray(newFileBytes)
newFile.write(newFileByteArray)

如果您使用的是Python 3.x,则可以bytes改用(也许应该这样做,因为它可以更好地表明您的意图)。但是在Python 2.x中,这bytes是行不通的,因为它只是的别名str。像往常一样,使用交互式解释器进行显示比使用文本进行解释要容易,所以让我这样做。

Python 3.x:

>>> bytearray(newFileBytes)
bytearray(b'{\x03\xff\x00d')
>>> bytes(newFileBytes)
b'{\x03\xff\x00d'

Python 2.x:

>>> bytearray(newFileBytes)
bytearray(b'{\x03\xff\x00d')
>>> bytes(newFileBytes)
'[123, 3, 255, 0, 100]'

This is exactly what bytearray is for:

newFileByteArray = bytearray(newFileBytes)
newFile.write(newFileByteArray)

If you’re using Python 3.x, you can use bytes instead (and probably ought to, as it signals your intention better). But in Python 2.x, that won’t work, because bytes is just an alias for str. As usual, showing with the interactive interpreter is easier than explaining with text, so let me just do that.

Python 3.x:

>>> bytearray(newFileBytes)
bytearray(b'{\x03\xff\x00d')
>>> bytes(newFileBytes)
b'{\x03\xff\x00d'

Python 2.x:

>>> bytearray(newFileBytes)
bytearray(b'{\x03\xff\x00d')
>>> bytes(newFileBytes)
'[123, 3, 255, 0, 100]'

回答 1

使用struct.pack的整数值转换成二进制字节,然后写入字节。例如

newFile.write(struct.pack('5B', *newFileBytes))

但是,我永远不会给二进制文件.txt扩展名。

此方法的好处是它也适用于其他类型,例如,如果任何值大于255,都可以使用'5i'该格式代替获取完整的32位整数。

Use struct.pack to convert the integer values into binary bytes, then write the bytes. E.g.

newFile.write(struct.pack('5B', *newFileBytes))

However I would never give a binary file a .txt extension.

The benefit of this method is that it works for other types as well, for example if any of the values were greater than 255 you could use '5i' for the format instead to get full 32-bit integers.


回答 2

要将<256的整数转换为二进制,请使用chr函数。因此,您正在考虑执行以下操作。

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))

To convert from integers < 256 to binary, use the chr function. So you’re looking at doing the following.

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))

回答 3

从Python 3.2+开始,您还可以使用本to_bytes机int方法完成此操作:

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
for byte in newFileBytes:
    newFile.write(byte.to_bytes(1, byteorder='big'))

也就是说,to_bytes在这种情况下,每次调用都会创建一个长度为1的字符串,其字符以大端顺序排列(对于长度为1的字符串而言,这是微不足道的),它表示整数值byte。您还可以将最后两行缩短为一行:

newFile.write(''.join([byte.to_bytes(1, byteorder='big') for byte in newFileBytes]))

As of Python 3.2+, you can also accomplish this using the to_bytes native int method:

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
for byte in newFileBytes:
    newFile.write(byte.to_bytes(1, byteorder='big'))

I.e., each single call to to_bytes in this case creates a string of length 1, with its characters arranged in big-endian order (which is trivial for length-1 strings), which represents the integer value byte. You can also shorten the last two lines into a single one:

newFile.write(''.join([byte.to_bytes(1, byteorder='big') for byte in newFileBytes]))

回答 4

您可以使用以下使用Python 3语法的代码示例:

from struct import pack
with open("foo.bin", "wb") as file:
  file.write(pack("<IIIII", *bytearray([120, 3, 255, 0, 100])))

这是外壳一线:

python -c $'from struct import pack\nwith open("foo.bin", "wb") as file: file.write(pack("<IIIII", *bytearray([120, 3, 255, 0, 100])))'

You can use the following code example using Python 3 syntax:

from struct import pack
with open("foo.bin", "wb") as file:
  file.write(pack("<IIIII", *bytearray([120, 3, 255, 0, 100])))

Here is shell one-liner:

python -c $'from struct import pack\nwith open("foo.bin", "wb") as file: file.write(pack("<IIIII", *bytearray([120, 3, 255, 0, 100])))'

回答 5

像这样使用泡菜:导入泡菜

您的代码如下所示:

import pickle
mybytes = [120, 3, 255, 0, 100]
with open("bytesfile", "wb") as mypicklefile:
    pickle.dump(mybytes, mypicklefile)

要读回数据,请使用pickle.load方法

Use pickle, like this: import pickle

Your code would look like this:

import pickle
mybytes = [120, 3, 255, 0, 100]
with open("bytesfile", "wb") as mypicklefile:
    pickle.dump(mybytes, mypicklefile)

To read the data back, use the pickle.load method


django更改默认运行服务器端口

问题:django更改默认运行服务器端口

我想在manage.py runserver侦听器中指定可侦听的默认端口config.ini。是否有比sys.argv在内部解析manage.py并插入配置的端口更容易的修复方法?

目标是运行时./manage.py runserver不必每次都指定地址和端口,而要从中获取参数config.ini

I would like to make the default port that manage.py runserver listens on specifiable in an extraneous config.ini. Is there an easier fix than parsing sys.argv inside manage.py and inserting the configured port?

The goal is to run ./manage.py runserver without having to specify address and port every time but having it take the arguments from the config.ini.


回答 0

使用以下命令创建一个bash脚本:

#!/bin/bash
exec ./manage.py runserver 0.0.0.0:<your_port>

将其保存为runserver并与manage.py放在同一目录中

chmod +x runserver

并运行为

./runserver

create a bash script with the following:

#!/bin/bash
exec ./manage.py runserver 0.0.0.0:<your_port>

save it as runserver in the same dir as manage.py

chmod +x runserver

and run it as

./runserver

回答 1

实际上,在开发Django服务器中更改(仅)端口的最简单方法是:

python manage.py runserver 7000

应该在http://127.0.0.1:7000/上运行开发服务器的服务器

Actually the easiest way to change (only) port in development Django server is just like:

python manage.py runserver 7000

that should run development server on http://127.0.0.1:7000/


回答 2

从Django 1.9开始,我发现的最简单的解决方案(基于Quentin Stafford-Fraser的解决方案)是manage.py在调用runserver命令之前添加几行以动态修改默认端口号:

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.dev")

    import django
    django.setup()

    # Override default port for `runserver` command
    from django.core.management.commands.runserver import Command as runserver
    runserver.default_port = "8080"

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

As of Django 1.9, the simplest solution I have found (based on Quentin Stafford-Fraser’s solution) is to add a few lines to manage.py which dynamically modify the default port number before invoking the runserver command:

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.dev")

    import django
    django.setup()

    # Override default port for `runserver` command
    from django.core.management.commands.runserver import Command as runserver
    runserver.default_port = "8080"

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

回答 3

运行django时,以下所有命令都可以更改端口:

python manage.py runserver 127.0.0.1:7000

python manage.py runserver 7000

python manage.py runserver 0:7000

All of the following commands are possible to change the port while running django:

python manage.py runserver 127.0.0.1:7000

python manage.py runserver 7000

python manage.py runserver 0:7000

回答 4

创建的子类django.core.management.commands.runserver.Command并覆盖default_port成员。将文件另存为自己的管理命令,例如<app-name>/management/commands/runserver.py

from django.conf import settings
from django.core.management.commands import runserver

class Command(runserver.Command):
    default_port = settings.RUNSERVER_PORT

我在此处加载默认端口表单设置(依次读取其他配置文件),但是您也可以直接从其他文件中读取它。

Create a subclass of django.core.management.commands.runserver.Command and overwrite the default_port member. Save the file as a management command of your own, e.g. under <app-name>/management/commands/runserver.py:

from django.conf import settings
from django.core.management.commands import runserver

class Command(runserver.Command):
    default_port = settings.RUNSERVER_PORT

I’m loading the default port form settings here (which in turn reads other configuration files), but you could just as well read it from some other file directly.


回答 5

我们创建了一个新的“ runserver”管理命令,该命令是标准包装的薄包装,但更改了默认端口。大致来说,您创建management/commands/runserver.py并放入了以下内容:

# Override the value of the constant coded into django...
import django.core.management.commands.runserver as runserver
runserver.DEFAULT_PORT="8001"

# ...print out a warning...
# (This gets output twice because runserver fires up two threads (one for autoreload).
#  We're living with it for now :-)
import os
dir_path = os.path.splitext(os.path.relpath(__file__))[0]
python_path = dir_path.replace(os.sep, ".")
print "Using %s with default port %s" % (python_path, runserver.DEFAULT_PORT)

# ...and then just import its standard Command class.
# Then manage.py runserver behaves normally in all other regards.
from django.core.management.commands.runserver import Command

We created a new ‘runserver’ management command which is a thin wrapper around the standard one but changes the default port. Roughly, you create management/commands/runserver.py and put in something like this:

# Override the value of the constant coded into django...
import django.core.management.commands.runserver as runserver
runserver.DEFAULT_PORT="8001"

# ...print out a warning...
# (This gets output twice because runserver fires up two threads (one for autoreload).
#  We're living with it for now :-)
import os
dir_path = os.path.splitext(os.path.relpath(__file__))[0]
python_path = dir_path.replace(os.sep, ".")
print "Using %s with default port %s" % (python_path, runserver.DEFAULT_PORT)

# ...and then just import its standard Command class.
# Then manage.py runserver behaves normally in all other regards.
from django.core.management.commands.runserver import Command

回答 6

在Pycharm中,您只需将端口添加到参数中

In Pycharm you can simply add the port to the parameters


回答 7

我在这里参加聚会很晚,但是如果您使用PyCharm这样的IDE,则在“运行”菜单下的“编辑配置”中有一个选项(“运行”>“编辑配置”),您可以在其中指定默认端口。当然,这仅在通过PyCharm进行调试/测试时才有意义。

I’m very late to the party here, but if you use an IDE like PyCharm, there’s an option in ‘Edit Configurations’ under the ‘Run’ menu (Run > Edit Configurations) where you can specify a default port. This of course is relevant only if you are debugging/testing through PyCharm.


回答 8

如果您想更改默认配置,请按照以下步骤操作:

  1. 打开终端类型命令

     $ /usr/local/lib/python<2/3>.x/dist-packages/django/core/management/commands
  2. 现在以超级用户身份在nano编辑器中打开runserver.py文件

     $ sudo nano runserver.py
  3. 找到“ default_port”变量,您将看到默认端口号为“ 8000”。现在,您可以将其更改为所需的任何内容。

  4. 现在退出并使用“ CTRL + X和Y保存文件”保存文件

注意:将<2/3> .x替换为可用的python版本

If you wish to change the default configurations then follow this steps:

  1. Open terminal type command

     $ /usr/local/lib/python<2/3>.x/dist-packages/django/core/management/commands
    
  2. Now open runserver.py file in nano editor as superuser

     $ sudo nano runserver.py
    
  3. find the ‘default_port’ variable then you will see the default port no is ‘8000’. Now you can change it to whatever you want.

  4. Now exit and save the file using “CTRL + X and Y to save the file”

Note: Replace <2/3>.x with your usable version of python


回答 9

  1. 在您的.bashrc中创建环境变量

    导出RUNSERVER_PORT = 8010

  2. 建立别名

    别名runserver =’django-admin runserver $ RUNSERVER_PORT’

我正在使用zsh和virtualenvs包装器。我将导出放入项目的postactivate脚本中,并为每个项目分配端口。

workon someproject
runserver
  1. Create enviroment variable in your .bashrc

    export RUNSERVER_PORT=8010

  2. Create alias

    alias runserver=’django-admin runserver $RUNSERVER_PORT’

Im using zsh and virtualenvs wrapper. I put export in projects postactivate script and asign port for every project.

workon someproject
runserver

回答 10

这是旧文章,但对那些感兴趣的人来说:

如果要更改默认端口号,以便在运行“ runserver”命令时从首选端口开始,请执行以下操作:

  1. 找到您的python安装。(您可以安装多个python,也可以安装虚拟环境版本,因此请确保找到正确的版本)
  2. 在python文件夹内找到site-packages文件夹。在其中,您将找到django安装
  3. 打开django文件夹->核心->管理->命令
  4. 在命令文件夹内,使用文本编辑器打开runserver.py脚本
  5. 找到DEFAULT_PORT字段。默认情况下等于8000。更改为您喜欢的任何内容 DEFAULT_PORT = "8080"
  6. 重新启动服务器:python manage.py runserver并查看它是否使用了您设置的端口号

它适用于python 2.7,但也应适用于新版本的python。祝好运

This is an old post but for those who are interested:

If you want to change the default port number so when you run the “runserver” command you start with your preferred port do this:

  1. Find your python installation. (you can have multiple pythons installed and you can have your virtual environment version as well so make sure you find the right one)
  2. Inside the python folder locate the site-packages folder. Inside that you will find your django installation
  3. Open the django folder-> core -> management -> commands
  4. Inside the commands folder open up the runserver.py script with a text editor
  5. Find the DEFAULT_PORT field. it is equal to 8000 by default. Change it to whatever you like DEFAULT_PORT = "8080"
  6. Restart your server: python manage.py runserver and see that it uses your set port number

It works with python 2.7 but it should work with newer versions of python as well. Good luck


回答 11

我在同一个问题上挣扎,找到了一个解决方案。我想它可以帮助您。运行时python manage.py runserver,它将在默认ip地址中使用127.0.0.1,在python环境中配置默认​​端口8000。在您的python设置中,转到<your python env>\Lib\site-packages\django\core\management\commands\runserver.py并设置1。2 default_port = '<your_port>'
.在def handle下找到并设置
if not options.get('addrport'): self.addr = '0.0.0.0' self.port = self.default_port

现在,如果您运行“ python manage.py runserver”,则它将默认在“ 0.0.0.0”上运行:

享受编码…..

I was struggling with the same problem and found one solution. I guess it can help you. when you run python manage.py runserver, it will take 127.0.0.1 as default ip address and 8000 as default port number which can be configured in your python environment. In your python setting, go to <your python env>\Lib\site-packages\django\core\management\commands\runserver.py and set 1. default_port = '<your_port>'
2. find this under def handle and set
if not options.get('addrport'): self.addr = '0.0.0.0' self.port = self.default_port

Now if you run “python manage.py runserver” it will run by default on “0.0.0.0:

Enjoy coding …..


有趣好用的Python教程

退出移动版
微信支付
请使用 微信 扫码支付