标签归档:language-features

python内部类的目的是什么?

问题:python内部类的目的是什么?

Python的内部/嵌套类使我感到困惑。没有他们,有什么事情是无法完成的吗?如果是这样,那是什么东西?

Python’s inner/nested classes confuse me. Is there something that can’t be accomplished without them? If so, what is that thing?


回答 0

引用自http://www.geekinterview.com/question_details/64739

内部阶层的优势:

  • 类的逻辑分组:如果一个类仅对另一个类有用,那么将其嵌入该类并将二者保持在一起是合乎逻辑的。嵌套此类“帮助程序类”可使它们的程序包更加简化。
  • 增加封装:考虑两个顶级类A和B,其中B需要访问A的成员,否则将其声明为私有。通过将B类隐藏在AA类中,可以将其成员声明为私有,B可以访问它们。另外,B本身可以对外界隐藏。
  • 更具可读性和可维护性的代码:在顶级类中嵌套小类会使代码更靠近使用位置。

主要优势是组织。可以用内部类实现什么没有他们来完成。

Quoted from http://www.geekinterview.com/question_details/64739:

Advantages of inner class:

  • Logical grouping of classes: If a class is useful to only one other class then it is logical to embed it in that class and keep the two together. Nesting such “helper classes” makes their package more streamlined.
  • Increased encapsulation: Consider two top-level classes A and B where B needs access to members of A that would otherwise be declared private. By hiding class B within class A A’s members can be declared private and B can access them. In addition B itself can be hidden from the outside world.
  • More readable, maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.

The main advantage is organization. Anything that can be accomplished with inner classes can be accomplished without them.


回答 1

没有他们,有什么事情是无法完成的吗?

不。它们绝对等同于通常在顶层定义类,然后将对它的引用复制到外部类中。

我认为“允许”嵌套类没有任何特殊原因,除了明确禁止“禁止”嵌套类没有特殊意义。

如果您正在寻找一个在外部/所有者对象的生命周期内存在的类,并且始终引用外部类的实例(内部类就像Java一样),那么Python的嵌套类就不是问题。但是您可以破解类似的东西:

import weakref, new

class innerclass(object):
    """Descriptor for making inner classes.

    Adds a property 'owner' to the inner class, pointing to the outer
    owner instance.
    """

    # Use a weakref dict to memoise previous results so that
    # instance.Inner() always returns the same inner classobj.
    #
    def __init__(self, inner):
        self.inner= inner
        self.instances= weakref.WeakKeyDictionary()

    # Not thread-safe - consider adding a lock.
    #
    def __get__(self, instance, _):
        if instance is None:
            return self.inner
        if instance not in self.instances:
            self.instances[instance]= new.classobj(
                self.inner.__name__, (self.inner,), {'owner': instance}
            )
        return self.instances[instance]


# Using an inner class
#
class Outer(object):
    @innerclass
    class Inner(object):
        def __repr__(self):
            return '<%s.%s inner object of %r>' % (
                self.owner.__class__.__name__,
                self.__class__.__name__,
                self.owner
            )

>>> o1= Outer()
>>> o2= Outer()
>>> i1= o1.Inner()
>>> i1
<Outer.Inner inner object of <__main__.Outer object at 0x7fb2cd62de90>>
>>> isinstance(i1, Outer.Inner)
True
>>> isinstance(i1, o1.Inner)
True
>>> isinstance(i1, o2.Inner)
False

(这使用了类装饰器,这是Python 2.6和3.0中的新功能。否则,您必须在类定义之后说“ Inner = innerclass(Inner)”。)

Is there something that can’t be accomplished without them?

No. They are absolutely equivalent to defining the class normally at top level, and then copying a reference to it into the outer class.

I don’t think there’s any special reason nested classes are ‘allowed’, other than it makes no particular sense to explicitly ‘disallow’ them either.

If you’re looking for a class that exists within the lifecycle of the outer/owner object, and always has a reference to an instance of the outer class — inner classes as Java does it – then Python’s nested classes are not that thing. But you can hack up something like that thing:

import weakref, new

class innerclass(object):
    """Descriptor for making inner classes.

    Adds a property 'owner' to the inner class, pointing to the outer
    owner instance.
    """

    # Use a weakref dict to memoise previous results so that
    # instance.Inner() always returns the same inner classobj.
    #
    def __init__(self, inner):
        self.inner= inner
        self.instances= weakref.WeakKeyDictionary()

    # Not thread-safe - consider adding a lock.
    #
    def __get__(self, instance, _):
        if instance is None:
            return self.inner
        if instance not in self.instances:
            self.instances[instance]= new.classobj(
                self.inner.__name__, (self.inner,), {'owner': instance}
            )
        return self.instances[instance]


# Using an inner class
#
class Outer(object):
    @innerclass
    class Inner(object):
        def __repr__(self):
            return '<%s.%s inner object of %r>' % (
                self.owner.__class__.__name__,
                self.__class__.__name__,
                self.owner
            )

>>> o1= Outer()
>>> o2= Outer()
>>> i1= o1.Inner()
>>> i1
<Outer.Inner inner object of <__main__.Outer object at 0x7fb2cd62de90>>
>>> isinstance(i1, Outer.Inner)
True
>>> isinstance(i1, o1.Inner)
True
>>> isinstance(i1, o2.Inner)
False

(This uses class decorators, which are new in Python 2.6 and 3.0. Otherwise you’d have to say “Inner= innerclass(Inner)” after the class definition.)


回答 2

您需要包裹一些东西才能理解这一点。在大多数语言中,类定义是对编译器的指令。也就是说,该类是在程序运行之前创建的。在python中,所有语句都是可执行的。这意味着该语句:

class foo(object):
    pass

是一条在运行时执行的语句,如下所示:

x = y + z

这意味着您不仅可以在其他类中创建类,还可以在任意位置创建类。考虑以下代码:

def foo():
    class bar(object):
        ...
    z = bar()

因此,“内部类”的想法实际上不是一种语言构造;这是一个程序员构造。圭多拥有这是怎么围绕很好的总结在这里。但本质上,基本思想是简化了语言的语法。

There’s something you need to wrap your head around to be able to understand this. In most languages, class definitions are directives to the compiler. That is, the class is created before the program is ever run. In python, all statements are executable. That means that this statement:

class foo(object):
    pass

is a statement that is executed at runtime just like this one:

x = y + z

This means that not only can you create classes within other classes, you can create classes anywhere you want to. Consider this code:

def foo():
    class bar(object):
        ...
    z = bar()

Thus, the idea of an “inner class” isn’t really a language construct; it’s a programmer construct. Guido has a very good summary of how this came about here. But essentially, the basic idea is this simplifies the language’s grammar.


回答 3

在类中嵌套类:

  • 嵌套类使类定义变得肿,这使得很难看到发生了什么。

  • 嵌套类会创建耦合,从而使测试更加困难。

  • 在Python中,您可以在文件/模块中放置一个以上的类,这与Java不同,因此该类仍保持与顶级类的距离,甚至可以在类名前加上“ _”,以帮助表示不应将其他类使用它。

嵌套类可以证明有用的地方是函数内

def some_func(a, b, c):
   class SomeClass(a):
      def some_method(self):
         return b
   SomeClass.__doc__ = c
   return SomeClass

该类从函数中捕获值,使您可以动态创建一个类,例如C ++中的模板元编程

Nesting classes within classes:

  • Nested classes bloat the class definition making it harder to see whats going on.

  • Nested classes can create coupling that would make testing more difficult.

  • In Python you can put more than one class in a file/module, unlike Java, so the class still remains close to top level class and could even have the class name prefixed with an “_” to help signify that others shouldn’t be using it.

The place where nested classes can prove useful is within functions

def some_func(a, b, c):
   class SomeClass(a):
      def some_method(self):
         return b
   SomeClass.__doc__ = c
   return SomeClass

The class captures the values from the function allowing you to dynamically create a class like template metaprogramming in C++


回答 4

我了解反对嵌套类的参数,但是在某些情况下有使用它们的情况。想象一下,我正在创建一个双向链接列表类,并且需要创建一个节点类来维护节点。我有两个选择,在DoublyLinkedList类内部创建Node类,或在DoublyLinkedList类外部创建Node类。在这种情况下,我首选第一种选择,因为Node类仅在DoublyLinkedList类内部有意义。虽然没有隐藏/封装的好处,但是可以说Node类是DoublyLinkedList类的一部分,这有一个分组的好处。

I understand the arguments against nested classes, but there is a case for using them in some occasions. Imagine I’m creating a doubly-linked list class, and I need to create a node class for maintaing the nodes. I have two choices, create Node class inside the DoublyLinkedList class, or create the Node class outside the DoublyLinkedList class. I prefer the first choice in this case, because the Node class is only meaningful inside the DoublyLinkedList class. While there’s no hiding/encapsulation benefit, there is a grouping benefit of being able to say the Node class is part of the DoublyLinkedList class.


回答 5

没有他们,有什么事情是无法完成的吗?如果是这样,那是什么东西?

有以下一些事情是不容易完成的相关类的继承

这是相关类A和的极简示例B

class A(object):
    class B(object):
        def __init__(self, parent):
            self.parent = parent

    def make_B(self):
        return self.B(self)


class AA(A):  # Inheritance
    class B(A.B):  # Inheritance, same class name
        pass

这段代码导致了一个相当合理和可预测的行为:

>>> type(A().make_B())
<class '__main__.A.B'>
>>> type(A().make_B().parent)
<class '__main__.A'>
>>> type(AA().make_B())
<class '__main__.AA.B'>
>>> type(AA().make_B().parent)
<class '__main__.AA'>

如果B是顶级类,则不能self.B()在方法中make_B编写B(),而只会写,从而失去与适当类的动态绑定

请注意,在此构造中,您永远不要在class A主体中引用class B。这是parent在课堂上介绍该属性的动机B

当然,可以在没有内部类的情况下重新创建此动态绑定,而这会浪费乏味且易于出错的类。

Is there something that can’t be accomplished without them? If so, what is that thing?

There is something that cannot be easily done without: inheritance of related classes.

Here is a minimalist example with the related classes A and B:

class A(object):
    class B(object):
        def __init__(self, parent):
            self.parent = parent

    def make_B(self):
        return self.B(self)


class AA(A):  # Inheritance
    class B(A.B):  # Inheritance, same class name
        pass

This code leads to a quite reasonable and predictable behaviour:

>>> type(A().make_B())
<class '__main__.A.B'>
>>> type(A().make_B().parent)
<class '__main__.A'>
>>> type(AA().make_B())
<class '__main__.AA.B'>
>>> type(AA().make_B().parent)
<class '__main__.AA'>

If B were a top-level class, you could not write self.B() in the method make_B but would simply write B(), and thus lose the dynamic binding to the adequate classes.

Note that in this construction, you should never refer to class A in the body of class B. This is the motivation for introducing the parent attribute in class B.

Of course, this dynamic binding can be recreated without inner class at the cost of a tedious and error-prone instrumentation of the classes.


回答 6

我使用它的主要用例是防止小模块的扩散,在不需要单独的模块时防止命名空间污染。如果要扩展现有的类,但是该现有的类必须引用另一个应该始终与其耦合的子类。例如,我可能有一个utils.py其中包含许多帮助程序类的模块,这些模块不一定耦合在一起,但我想加强其中一些帮助程序类的耦合。例如,当我实现https://stackoverflow.com/a/8274307/2718295时

utils.py

import json, decimal

class Helper1(object):
    pass

class Helper2(object):
    pass

# Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats
class DecimalJSONEncoder(json.JSONEncoder):

    class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched
        def __init__(self, obj):
            self._obj = obj
        def __repr__(self):
            return '{:f}'.format(self._obj)

    def default(self, obj): # override JSONEncoder.default
        if isinstance(obj, decimal.Decimal):
            return self._repr_decimal(obj)
        # else
        super(self.__class__, self).default(obj)
        # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 

然后,我们可以:

>>> from utils import DecimalJSONEncoder
>>> import json, decimal
>>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), 
... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder)
{"key2": "key2_value", "key_1": 1.12345678901234}

当然,我们可以完全避开继承,json.JSONEnocder而只需覆盖default()即可:

import decimal, json

class Helper1(object):
    pass

def json_encoder_decimal(obj):
    class _repr_decimal(float):
        ...

    if isinstance(obj, decimal.Decimal):
        return _repr_decimal(obj)

    return json.JSONEncoder(obj)


>>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder)
'{"key1": 1.12345678901234}'

但有时只是出于约定,您希望utils由可扩展性的类组成。

这是另一个用例:我希望在OuterClass中创建一个用于可变项的工厂,而不必调用copy

class OuterClass(object):

    class DTemplate(dict):
        def __init__(self):
            self.update({'key1': [1,2,3],
                'key2': {'subkey': [4,5,6]})


    def __init__(self):
        self.outerclass_dict = {
            'outerkey1': self.DTemplate(),
            'outerkey2': self.DTemplate()}



obj = OuterClass()
obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4)
assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6]

我更喜欢这种模式,而@staticmethod不是原本用于工厂功能的装饰器。

The main use case I use this for is the prevent proliferation of small modules and to prevent namespace pollution when separate modules are not needed. If I am extending an existing class, but that existing class must reference another subclass that should always be coupled to it. For example, I may have a utils.py module that has many helper classes in it, that aren’t necessarily coupled together, but I want to reinforce coupling for some of those helper classes. For example, when I implement https://stackoverflow.com/a/8274307/2718295

:utils.py:

import json, decimal

class Helper1(object):
    pass

class Helper2(object):
    pass

# Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats
class DecimalJSONEncoder(json.JSONEncoder):

    class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched
        def __init__(self, obj):
            self._obj = obj
        def __repr__(self):
            return '{:f}'.format(self._obj)

    def default(self, obj): # override JSONEncoder.default
        if isinstance(obj, decimal.Decimal):
            return self._repr_decimal(obj)
        # else
        super(self.__class__, self).default(obj)
        # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 

Then we can:

>>> from utils import DecimalJSONEncoder
>>> import json, decimal
>>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), 
... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder)
{"key2": "key2_value", "key_1": 1.12345678901234}

Of course, we could have eschewed inheriting json.JSONEnocder altogether and just override default():

:

import decimal, json

class Helper1(object):
    pass

def json_encoder_decimal(obj):
    class _repr_decimal(float):
        ...

    if isinstance(obj, decimal.Decimal):
        return _repr_decimal(obj)

    return json.JSONEncoder(obj)


>>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder)
'{"key1": 1.12345678901234}'

But sometimes just for convention, you want utils to be composed of classes for extensibility.

Here’s another use-case: I want a factory for mutables in my OuterClass without having to invoke copy:

class OuterClass(object):

    class DTemplate(dict):
        def __init__(self):
            self.update({'key1': [1,2,3],
                'key2': {'subkey': [4,5,6]})


    def __init__(self):
        self.outerclass_dict = {
            'outerkey1': self.DTemplate(),
            'outerkey2': self.DTemplate()}



obj = OuterClass()
obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4)
assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6]

I prefer this pattern over the @staticmethod decorator you would otherwise use for a factory function.


回答 7

1.两种功能等效的方式

前面显示的两种方法在功能上是相同的。但是,有一些细微的差异,并且在某些情况下您想选择一个而不是另一个。

方式1:嵌套类定义
(=“ Nested class”)

class MyOuter1:
    class Inner:
        def show(self, msg):
            print(msg)

方式2:将模块级别的内部类附加到外部类
(=“ Referenced内部类”)

class _InnerClass:
    def show(self, msg):
        print(msg)

class MyOuter2:
    Inner = _InnerClass

下划线用于遵循PEP8: “内部接口(包,模块,类,函数,属性或其他名称)应-带有一个前导下划线作为前缀”。

2.相似之处

下面的代码片段演示了“嵌套类”与“引用内部类”的功能相似性;它们在检查内部类实例类型的代码中的行为方式相同。不用说,m.inner.anymethod()它们与m1和的行为类似m2

m1 = MyOuter1()
m2 = MyOuter2()

innercls1 = getattr(m1, 'Inner', None)
innercls2 = getattr(m2, 'Inner', None)

isinstance(innercls1(), MyOuter1.Inner)
# True

isinstance(innercls2(), MyOuter2.Inner)
# True

type(innercls1()) == mypackage.outer1.MyOuter1.Inner
# True (when part of mypackage)

type(innercls2()) == mypackage.outer2.MyOuter2.Inner
# True (when part of mypackage)

3.差异

下面列出了“嵌套类”和“引用内部类”的区别。它们并不大,但是有时您希望基于这些选择一个或另一个。

3.1代码封装

使用“嵌套类”可以比使用“引用内部类”更好地封装代码。模块命名空间中的类是全局变量。嵌套类的目的是减少模块中的混乱情况,并将内部类放入外部类中。

当没有人使用*时from packagename import *,少量模块级别的变量可能很好,例如,当使用具有代码完成/智能感知的IDE时。

* 对吗?

3.2代码的可读性

Django文档指示将内部类Meta用于模型元数据。指示框架用户class Foo(models.Model)使用inner 编写一个更清晰的* class Meta

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

而不是“写class _Meta,然后写一个class Foo(models.Model)Meta = _Meta”;

class _Meta:
    ordering = ["horn_length"]
    verbose_name_plural = "oxen"

class Ox(models.Model):
    Meta = _Meta
    horn_length = models.IntegerField()
  • 使用“嵌套类”方法,可以读取嵌套的项目符号点列表,但是使用“引用内部类”方法,则必须向上滚动以查看其定义,_Meta以查看其“子项”(属性)。

  • 如果您的代码嵌套级别增加或由于其他原因导致行很长,则“引用的内部类”方法可能更具可读性。

*当然是口味问题

3.3略有不同的错误消息

这没什么大不了的,只是为了完整性:当访问内部类的不存在属性时,我们会看到截然不同的异常。继续第2节中给出的示例:

innercls1.foo()
# AttributeError: type object 'Inner' has no attribute 'foo'

innercls2.foo()
# AttributeError: type object '_InnerClass' has no attribute 'foo'

这是因为type内部类的s是

type(innercls1())
#mypackage.outer1.MyOuter1.Inner

type(innercls2())
#mypackage.outer2._InnerClass

1. Two functionally equivalent ways

The two ways shown before are functionally identical. However, there are some subtle differences, and there are situations when you would like to choose one over another.

Way 1: Nested class definition
(=”Nested class”)

class MyOuter1:
    class Inner:
        def show(self, msg):
            print(msg)

Way 2: With module level Inner class attached to Outer class
(=”Referenced inner class”)

class _InnerClass:
    def show(self, msg):
        print(msg)

class MyOuter2:
    Inner = _InnerClass

Underscore is used to follow PEP8 “internal interfaces (packages, modules, classes, functions, attributes or other names) should — be prefixed with a single leading underscore.”

2. Similarities

Below code snippet demonstrates the functional similarities of the “Nested class” vs “Referenced inner class”; They would behave the same way in code checking for the type of an inner class instance. Needless to say, the m.inner.anymethod() would behave similarly with m1 and m2

m1 = MyOuter1()
m2 = MyOuter2()

innercls1 = getattr(m1, 'Inner', None)
innercls2 = getattr(m2, 'Inner', None)

isinstance(innercls1(), MyOuter1.Inner)
# True

isinstance(innercls2(), MyOuter2.Inner)
# True

type(innercls1()) == mypackage.outer1.MyOuter1.Inner
# True (when part of mypackage)

type(innercls2()) == mypackage.outer2.MyOuter2.Inner
# True (when part of mypackage)

3. Differences

The differences of “Nested class” and “Referenced inner class” are listed below. They are not big, but sometimes you would like to choose one or the other based on these.

3.1 Code Encapsulation

With “Nested classes” it is possible to encapsulate code better than with “Referenced inner class”. A class in the module namespace is a global variable. The purpose of nested classes is to reduce clutter in the module and put the inner class inside the outer class.

While no-one* is using from packagename import *, low amount of module level variables can be nice for example when using an IDE with code completion / intellisense.

*Right?

3.2 Readability of code

Django documentation instructs to use inner class Meta for model metadata. It is a bit more clearer* to instruct the framework users to write a class Foo(models.Model) with inner class Meta;

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

instead of “write a class _Meta, then write a class Foo(models.Model) with Meta = _Meta“;

class _Meta:
    ordering = ["horn_length"]
    verbose_name_plural = "oxen"

class Ox(models.Model):
    Meta = _Meta
    horn_length = models.IntegerField()
  • With the “Nested class” approach the code can be read a nested bullet point list, but with the “Referenced inner class” method one has to scroll back up to see the definition of _Meta to see its “child items” (attributes).

  • The “Referenced inner class” method can be more readable if your code nesting level grows or the rows are long for some other reason.

* Of course, a matter of taste

3.3 Slightly different error messages

This is not a big deal, but just for completeness: When accessing non-existent attribute for the inner class, we see slighly different exceptions. Continuing the example given in Section 2:

innercls1.foo()
# AttributeError: type object 'Inner' has no attribute 'foo'

innercls2.foo()
# AttributeError: type object '_InnerClass' has no attribute 'foo'

This is because the types of the inner classes are

type(innercls1())
#mypackage.outer1.MyOuter1.Inner

type(innercls2())
#mypackage.outer2._InnerClass

回答 8

我已经使用Python的内部类在unittest函数(即内部def test_something():)中故意创建了错误的子类,以便接近100%的测试覆盖率(例如,通过覆盖某些方法来测试很少触发的日志语句)。

回想起来,它类似于埃德的答案https://stackoverflow.com/a/722036/1101109

一旦删除了所有内部引用,此类内部类便超出范围,并准备进行垃圾回收。例如,获取以下inner.py文件:

class A(object):
    pass

def scope():
    class Buggy(A):
        """Do tests or something"""
    assert isinstance(Buggy(), A)

在OSX Python 2.7.6下得到以下奇怪结果:

>>> from inner import A, scope
>>> A.__subclasses__()
[]
>>> scope()
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A, scope
>>> from inner import A
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A
>>> import gc
>>> gc.collect()
0
>>> gc.collect()  # Yes I needed to call the gc twice, seems reproducible
3
>>> from inner import A
>>> A.__subclasses__()
[]

提示-不要继续尝试使用Django模型,这似乎保留了其他(已缓存?)对我的越野车类的引用。

因此,总的来说,我不建议您将内部类用于此类目的,除非您确实确实认为100%的测试覆盖率并且不能使用其他方法。虽然我觉得很高兴知道,如果使用__subclasses__(),它有时会被内部类污染。无论哪种方式,如果您走了这么远,我都认为到目前为止,我们对Python,私有dunderscores以及所有内容都非常了解。

I have used Python’s inner classes to create deliberately buggy subclasses within unittest functions (i.e. inside def test_something():) in order to get closer to 100% test coverage (e.g. testing very rarely triggered logging statements by overriding some methods).

In retrospect it’s similar to Ed’s answer https://stackoverflow.com/a/722036/1101109

Such inner classes should go out of scope and be ready for garbage collection once all references to them have been removed. For instance, take the following inner.py file:

class A(object):
    pass

def scope():
    class Buggy(A):
        """Do tests or something"""
    assert isinstance(Buggy(), A)

I get the following curious results under OSX Python 2.7.6:

>>> from inner import A, scope
>>> A.__subclasses__()
[]
>>> scope()
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A, scope
>>> from inner import A
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A
>>> import gc
>>> gc.collect()
0
>>> gc.collect()  # Yes I needed to call the gc twice, seems reproducible
3
>>> from inner import A
>>> A.__subclasses__()
[]

Hint – Don’t go on and try doing this with Django models, which seemed to keep other (cached?) references to my buggy classes.

So in general, I wouldn’t recommend using inner classes for this kind of purpose unless you really do value that 100% test coverage and can’t use other methods. Though I think it’s nice to be aware that if you use the __subclasses__(), that it can sometimes get polluted by inner classes. Either way if you followed this far, I think we’re pretty deep into Python at this point, private dunderscores and all.


为什么python dict.update()不返回对象?

问题:为什么python dict.update()不返回对象?

我正在尝试:

award_dict = {
    "url" : "http://facebook.com",
    "imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
    "count" : 1,
}

def award(name, count, points, desc_string, my_size, parent) :
    if my_size > count :
        a = {
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }
        a.update(award_dict)
        return self.add_award(a, siteAlias, alias).award

但是,如果觉得该函数真的很麻烦,我宁愿这样做:

        return self.add_award({
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }.update(award_dict), siteAlias, alias).award

为什么不更新返回对象,以便您可以链接?

JQuery这样做是为了进行链接。为什么在python中不可接受?

I ‘m trying to do :

award_dict = {
    "url" : "http://facebook.com",
    "imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
    "count" : 1,
}

def award(name, count, points, desc_string, my_size, parent) :
    if my_size > count :
        a = {
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }
        a.update(award_dict)
        return self.add_award(a, siteAlias, alias).award

But if felt really cumbersome in the function, and I would have rather done :

        return self.add_award({
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }.update(award_dict), siteAlias, alias).award

Why doesn’t update return the object so you can chain?

JQuery does this to do chaining. Why isn’t it acceptable in python?


回答 0

Python大多实现了务实的命令查询分离风格:mutator返回None(带有务实的异常,例如pop;-),因此它们不可能与访问器混淆(同样,赋值不是表达式,语句-表达式分离,依此类推)。

这并不意味着没有很多方法可以在您真正想要的时候将它们合并,例如,dict(a, **award_dict)做出一个新的字典,就像您希望.update返回的字典一样。所以,如果您真的觉得很重要,那就为什么不使用THAT ?

编辑:顺便说一句,在您的特定情况下,无需a按照以下方式进行创建:

dict(name=name, description=desc % count, points=points, parent_award=parent,
     **award_dict)

创建一个具有与您的语义完全相同的语义的dict a.update(award_dict)(包括在发生冲突的情况下,in中的条目award_dict会覆盖您明确提供的条目的事实;要获取其他语义,即,使显式条目“赢得”此类冲突,award_dict作为唯一的位置 arg 传递,关键字“>” 之前传递,并丧失**形式- dict(award_dict, name=name等等)。

Python’s mostly implementing a pragmatically tinged flavor of command-query separation: mutators return None (with pragmatically induced exceptions such as pop;-) so they can’t possibly be confused with accessors (and in the same vein, assignment is not an expression, the statement-expression separation is there, and so forth).

That doesn’t mean there aren’t a lot of ways to merge things up when you really want, e.g., dict(a, **award_dict) makes a new dict much like the one you appear to wish .update returned — so why not use THAT if you really feel it’s important?

Edit: btw, no need, in your specific case, to create a along the way, either:

dict(name=name, description=desc % count, points=points, parent_award=parent,
     **award_dict)

creates a single dict with exactly the same semantics as your a.update(award_dict) (including, in case of conflicts, the fact that entries in award_dict override those you’re giving explicitly; to get the other semantics, i.e., to have explicit entries “winning” such conflicts, pass award_dict as the sole positional arg, before the keyword ones, and bereft of the ** form — dict(award_dict, name=name etc etc).


回答 1

按照惯例,Python的API区分过程和函数。函数根据其参数(包括任何目标对象)计算新值;过程会修改对象,并且不返回任何内容(即,它们返回None)。因此,程序具有副作用,而功能则没有。更新是一个过程,因此它不返回值。

这样做的动机是,否则可能会导致不良的副作用。考虑

bar = foo.reverse()

如果reverse(也将反向替换列表)也返回列表,则用户可能会认为reverse返回一个新列表,该列表已分配给bar,而永远不会注意到foo也被修改了。通过使反向返回为“无”,他们可以立即认识到bar不是反向的结果,并且看起来更接近反向的效果。

Python’s API, by convention, distinguishes between procedures and functions. Functions compute new values out of their parameters (including any target object); procedures modify objects and don’t return anything (i.e. they return None). So procedures have side effects, functions don’t. update is a procedure, hence it doesn’t return a value.

The motivation for doing it that way is that otherwise, you may get undesirable side effects. Consider

bar = foo.reverse()

If reverse (which reverses the list in-place) would also return the list, users may think that reverse returns a new list which gets assigned to bar, and never notice that foo also gets modified. By making reverse return None, they immediately recognize that bar is not the result of the reversal, and will look more close what the effect of reverse is.


回答 2

这很容易,因为:

(lambda d: d.update(dict2) or d)(d1)

This is easy as:

(lambda d: d.update(dict2) or d)(d1)

回答 3

>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

请注意,除了返回合并的字典外,它还会就地修改第一个参数。因此dict_merge(a,b)将修改a。

或者,当然,您可以全部内联:

>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}
>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

Note that as well as returning the merged dict, it modifies the first parameter in-place. So dict_merge(a,b) will modify a.

Or, of course, you can do it all inline:

>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

回答 4

没有足够的声誉来评论顶部答案

@beardc这似乎不是CPython。PyPy给我“ TypeError:关键字必须是字符串”

之所以**kwargs只能使用解决方案,是因为要合并的字典仅具有string类型的键

>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}

not enough reputation for comment left on top answer

@beardc this doesn’t seem to be CPython thing. PyPy gives me “TypeError: keywords must be strings”

The solution with **kwargs only works because the dictionary to be merged only has keys of type string.

i.e.

>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

vs

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}

回答 5

不是说它不被接受,而是不是那样dicts实现的。

如果您查看Django的ORM,它将充分利用链接。不劝阻它,您甚至可以继承dict并仅重写update以执行update和return self,如果您确实需要的话。

class myDict(dict):
    def update(self, *args):
        dict.update(self, *args)
        return self

Its not that it isn’t acceptable, but rather that dicts weren’t implemented that way.

If you look at Django’s ORM, it makes extensive use of chaining. Its not discouraged, you could even inherit from dict and only override update to do update and return self, if you really want it.

class myDict(dict):
    def update(self, *args):
        dict.update(self, *args)
        return self

回答 6

我会尽可能接近您建议的解决方案

from collections import ChainMap

return self.add_award(ChainMap(award_dict, {
    "name" : name,
    "description" : desc_string % count,
    "points" : points,
    "parent_award" : parent,
}), siteAlias, alias).award

as close to your proposed solution as I could get

from collections import ChainMap

return self.add_award(ChainMap(award_dict, {
    "name" : name,
    "description" : desc_string % count,
    "points" : points,
    "parent_award" : parent,
}), siteAlias, alias).award

回答 7

对于那些迟到的人,我已经安排了一些时间安排(Py 3.7),显示了.update()保留输入的基础方法看起来要快一点(约5%),而就地更新时则要快得多(约30%)。 。

像往常一样,所有基准都应加一粒盐。

def join2(dict1, dict2, inplace=False):
    result = dict1 if inplace else dict1.copy()
    result.update(dict2)
    return result


def join(*items):
    iter_items = iter(items)
    result = next(iter_items).copy()
    for item in iter_items:
        result.update(item)
    return result


def update_or(dict1, dict2):
    return dict1.update(dict2) or dict1


d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}

%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

就地操作的时序有些棘手,因此需要在额外的复制操作中进行修改(第一个时序仅供参考):

%timeit dd = d1.copy()
# 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

For those coming late to the party, I had put some timing together (Py 3.7), showing that .update() based methods look a bit (~5%) faster when inputs are preserved and noticeably (~30%) faster when just updating in-place.

As usual, all the benchmarks should be taken with a grain of salt.

def join2(dict1, dict2, inplace=False):
    result = dict1 if inplace else dict1.copy()
    result.update(dict2)
    return result


def join(*items):
    iter_items = iter(items)
    result = next(iter_items).copy()
    for item in iter_items:
        result.update(item)
    return result


def update_or(dict1, dict2):
    return dict1.update(dict2) or dict1


d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}

%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

The timings for the in-place operations are a bit trickier, so it would need to be modified along an extra copy operation (the first timing is just for reference):

%timeit dd = d1.copy()
# 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

回答 8

import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))
import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))

回答 9

刚在Python 3.4中尝试过此操作(因此无法使用高级{**dict_1, **dict_2}语法)。

我希望能够在字典中使用非字符串键,并提供任意数量的字典。

另外,我想制作一本新词典,所以我选择不使用collections.ChainMap(这是我dict.update最初不想使用的原因。

这是我最后写的:

def merge_dicts(*dicts):
    all_keys  = set(k for d in dicts for k in d.keys())
    chain_map = ChainMap(*reversed(dicts))
    return {k: chain_map[k] for k in all_keys}

merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}

Just been trying this myself in Python 3.4 (so wasn’t able to use the fancy {**dict_1, **dict_2} syntax).

I wanted to be able to have non-string keys in dictionaries as well as provide an arbitrary amount of dictionaries.

Also, I wanted to make a new dictionary so I opted to not use collections.ChainMap (kinda the reason I didn’t want to use dict.update initially.

Here’s what I ended up writing:

def merge_dicts(*dicts):
    all_keys  = set(k for d in dicts for k in d.keys())
    chain_map = ChainMap(*reversed(dicts))
    return {k: chain_map[k] for k in all_keys}

merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}

python“ with”语句的目的是什么?

问题:python“ with”语句的目的是什么?

with今天是第一次遇到Python 语句。我已经使用Python几个月了,甚至不知道它的存在!考虑到它的地位有些晦涩,我认为值得一问:

  1. Python with语句旨在用于什么?
  2. 你用它来做什么?
  3. 我需要了解任何陷阱,还是与其使用相关的常见反模式?有什么try..finally比这更好用的情况with吗?
  4. 为什么没有更广泛地使用它?
  5. 哪些标准库类与之兼容?

I came across the Python with statement for the first time today. I’ve been using Python lightly for several months and didn’t even know of its existence! Given its somewhat obscure status, I thought it would be worth asking:

  1. What is the Python with statement designed to be used for?
  2. What do you use it for?
  3. Are there any gotchas I need to be aware of, or common anti-patterns associated with its use? Any cases where it is better use try..finally than with?
  4. Why isn’t it used more widely?
  5. Which standard library classes are compatible with it?

回答 0

  1. 我相信这已经被我之前的其他用户回答了,所以我仅出于完整性的考虑添加该with语句:该语句通过将常见的准备和清理任务封装在所谓的上下文管理器中来简化异常处理。可以在PEP 343中找到更多详细信息。例如,该open语句本身就是一个上下文管理器,它使您可以打开文件,只要with在使用它的语句上下文中执行该文件,就可以保持打开状态,并在离开上下文后立即将其关闭,无论您是由于异常还是在常规控制流程中离开了它。with因此,可以使用类似于C ++中的RAII模式的方式使用该语句:with语句并在您离开with上下文时释放。

  2. 一些示例是:使用打开文件,使用with open(filename) as fp:获取锁with lock:(在lock的实例threading.Lock)。您还可以使用中的contextmanager装饰器来构造自己的上下文管理器contextlib。例如,当我不得不临时更改当前目录然后返回到原来的位置时,我经常使用它:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    这是另一个示例,该示例临时重定向sys.stdinsys.stdout并重定向sys.stderr到其他文件句柄并稍后将其还原:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    最后,另一个示例创建一个临时文件夹并在离开上下文时清理它:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    
  1. I believe this has already been answered by other users before me, so I only add it for the sake of completeness: the with statement simplifies exception handling by encapsulating common preparation and cleanup tasks in so-called context managers. More details can be found in PEP 343. For instance, the open statement is a context manager in itself, which lets you open a file, keep it open as long as the execution is in the context of the with statement where you used it, and close it as soon as you leave the context, no matter whether you have left it because of an exception or during regular control flow. The with statement can thus be used in ways similar to the RAII pattern in C++: some resource is acquired by the with statement and released when you leave the with context.

  2. Some examples are: opening files using with open(filename) as fp:, acquiring locks using with lock: (where lock is an instance of threading.Lock). You can also construct your own context managers using the contextmanager decorator from contextlib. For instance, I often use this when I have to change the current directory temporarily and then return to where I was:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    Here’s another example that temporarily redirects sys.stdin, sys.stdout and sys.stderr to some other file handle and restores them later:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    And finally, another example that creates a temporary folder and cleans it up when leaving the context:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    

回答 1

我会建议两个有趣的讲座:

  • PEP 343 “ with”声明
  • Effbot了解Python的“ with”语句

1.with语句用于使用上下文管理器定义的方法来包装块的执行。这允许将常用try...except...finally用法模式封装起来,以方便重用。

2. 您可以执行以下操作:

with open("foo.txt") as foo_file:
    data = foo_file.read()

要么

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

或(Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

要么

lock = threading.Lock()
with lock:
    # Critical section of code

3. 我在这里看不到任何反模式。
引用Dive进入Python

试试..最终是好的。与更好。

4. 我想这与程序员使用try..catch..finally其他语言的语句的习惯有关。

I would suggest two interesting lectures:

  • PEP 343 The “with” Statement
  • Effbot Understanding Python’s “with” statement

1. The with statement is used to wrap the execution of a block with methods defined by a context manager. This allows common try...except...finally usage patterns to be encapsulated for convenient reuse.

2. You could do something like:

with open("foo.txt") as foo_file:
    data = foo_file.read()

OR

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

OR (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

OR

lock = threading.Lock()
with lock:
    # Critical section of code

3. I don’t see any Antipattern here.
Quoting Dive into Python:

try..finally is good. with is better.

4. I guess it’s related to programmers’s habit to use try..catch..finally statement from other languages.


回答 2

Python with语句是Resource Acquisition Is InitializationC ++中常用的惯用语的内置语言支持。旨在允许安全获取和释放操作系统资源。

with语句在作用域/块内创建资源。您可以使用块中的资源编写代码。当该块退出时,无论该块中代码的结果如何(即该块是正常退出还是由于异常而退出),资源都会被干净地释放。

Python库中的许多资源都遵循该with语句所需的协议,因此可以立即使用。但是,任何人都可以通过实施有据可查的协议来制作可用于with语句的资源:PEP 0343

每当您在应用程序中获取必须明确放弃的资源(例如文件,网络连接,锁等)时,都应使用它。

The Python with statement is built-in language support of the Resource Acquisition Is Initialization idiom commonly used in C++. It is intended to allow safe acquisition and release of operating system resources.

The with statement creates resources within a scope/block. You write your code using the resources within the block. When the block exits the resources are cleanly released regardless of the outcome of the code in the block (that is whether the block exits normally or because of an exception).

Many resources in the Python library that obey the protocol required by the with statement and so can used with it out-of-the-box. However anyone can make resources that can be used in a with statement by implementing the well documented protocol: PEP 0343

Use it whenever you acquire resources in your application that must be explicitly relinquished such as files, network connections, locks and the like.


回答 3

再次为了完整性,我将添加最有用的with语句用例。

我进行了大量的科学计算,对于某些活动,我需要使用Decimal库来进行任意精度的计算。我的代码的某些部分需要高精度,而对于大多数其他部分,则需要较低的精度。

我将默认精度设置为一个较低的数字,然后使用with来获取某些部分的更精确答案:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

我在“超几何测试”中经常使用此功能,该测试需要将大量数字除以形式阶乘。在进行基因组比例计算时,必须注意舍入和溢出错误。

Again for completeness I’ll add my most useful use-case for with statements.

I do a lot of scientific computing and for some activities I need the Decimal library for arbitrary precision calculations. Some part of my code I need high precision and for most other parts I need less precision.

I set my default precision to a low number and then use with to get a more precise answer for some sections:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

I use this a lot with the Hypergeometric Test which requires the division of large numbers resulting form factorials. When you do genomic scale calculations you have to be careful of round-off and overflow errors.


回答 4

反模式的一个例子可能是使用with一个循环内时,它会更有效率有with外循环

例如

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

第一种方法是为每个文件打开和关闭文件,row而第二种方法是一次打开和关闭文件,这可能会导致性能问题。

An example of an antipattern might be to use the with inside a loop when it would be more efficient to have the with outside the loop

for example

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

vs

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

The first way is opening and closing the file for each row which may cause performance problems compared to the second way with opens and closes the file just once.


回答 5

参见PEP 343-‘with’语句,最后有一个示例部分。

… Python语言的新语句“ with”使排除try / finally语句的标准用法成为可能。

See PEP 343 – The ‘with’ statement, there is an example section at the end.

… new statement “with” to the Python language to make it possible to factor out standard uses of try/finally statements.


回答 6

第1、2和3点被合理地涵盖了:

4:它相对较新,仅在python2.6 +(或使用的python2.5 from __future__ import with_statement)中可用

points 1, 2, and 3 being reasonably well covered:

4: it is relatively new, only available in python2.6+ (or python2.5 using from __future__ import with_statement)


回答 7

with语句适用于所谓的上下文管理器:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

这个想法是通过在离开“ with”块之后进行必要的清理来简化异常处理。一些python内置插件已经可以用作上下文管理器。

The with statement works with so-called context managers:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

The idea is to simplify exception handling by doing the necessary cleanup after leaving the ‘with’ block. Some of the python built-ins already work as context managers.


回答 8

开箱即用支持的另一个示例是流行的数据库模块的对象,当您习惯于使用内置open()行为方式时,乍一看可能会感到困惑。connection

这些connection对象是上下文管理器,因此可以直接在中使用with-statement,但是在使用上述说明时,请注意:

with-block完成时,或者与异常或不,该连接不会关闭。万一with-block异常结束,则回滚事务,否则提交事务。

这意味着程序员必须注意自己关闭连接,但允许获取连接,并以多个方式使用它with-statements,如psycopg2 docs中所示:

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

在上面的示例中,您会注意到的cursor对象psycopg2也是上下文管理器。从有关行为的相关文档中:

cursor出口退出时,with-block它将关闭,释放最终与之关联的任何资源。交易状态不受影响。

Another example for out-of-the-box support, and one that might be a bit baffling at first when you are used to the way built-in open() behaves, are connection objects of popular database modules such as:

The connection objects are context managers and as such can be used out-of-the-box in a with-statement, however when using the above note that:

When the with-block is finished, either with an exception or without, the connection is not closed. In case the with-block finishes with an exception, the transaction is rolled back, otherwise the transaction is commited.

This means that the programmer has to take care to close the connection themselves, but allows to acquire a connection, and use it in multiple with-statements, as shown in the psycopg2 docs:

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

In the example above, you’ll note that the cursor objects of psycopg2 also are context managers. From the relevant documentation on the behavior:

When a cursor exits the with-block it is closed, releasing any resource eventually associated with it. The state of the transaction is not affected.


回答 9

在python中,通常使用“ with ”语句来打开文件,处理文件中存在的数据,以及不调用close()方法而关闭文件。“ with”语句通过提供清理活动使异常处理更加简单。

的一般形式:

with open(“file name”, mode”) as file-var:
    processing statements

注意:无需通过在file-var.close()上调用close()来关闭文件

In python generally “with” statement is used to open a file, process the data present in the file, and also to close the file without calling a close() method. “with” statement makes the exception handling simpler by providing cleanup activities.

General form of with:

with open(“file name”, “mode”) as file-var:
    processing statements

note: no need to close the file by calling close() upon file-var.close()