Python에서 인스턴스 변수의 기본값을 어떻게 선언해야합니까?
클래스 멤버에게 다음과 같은 기본값을 제공해야합니까?
class Foo:
num = 1
아니면 이것처럼?
class Foo:
def __init__(self):
self.num = 1
에서 이 질문에 나는, 두 경우 모두에서 그 발견
bar = Foo()
bar.num += 1
잘 정의 된 작업입니다.
첫 번째 방법은 클래스 변수를 제공하고 두 번째 방법은 제공하지 않는다는 것을 이해합니다. 그러나 클래스 변수가 필요하지 않고 인스턴스 변수에 대한 기본값 만 설정하면되는 경우 두 메서드 모두 똑같이 좋은가요? 아니면 그들 중 하나가 다른 것보다 더 '비단뱀 적'입니까?
내가 주목 한 한 가지는 Django 튜토리얼 에서 두 번째 방법을 사용하여 모델을 선언한다는 것입니다. 개인적으로 두 번째 방법이 더 우아하다고 생각하지만 '표준'방법이 무엇인지 알고 싶습니다.
bp의 대답을 확장하여 불변 유형이 의미하는 바를 보여주고 싶었습니다.
첫째, 이것은 괜찮습니다.
>>> class TestB():
... def __init__(self, attr=1):
... self.attr = attr
...
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1
그러나 이것은 변경 불가능한 (변경 불가능한) 유형에 대해서만 작동합니다. 기본값이 변경 가능한 경우 (바꿀 수 있음) 대신 다음과 같은 상황이 발생합니다.
>>> class Test():
... def __init__(self, attr=[]):
... self.attr = attr
...
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>>
a
와 둘 다 b
공유 속성이 있습니다. 이것은 종종 원치 않는 것입니다.
이것은 유형이 변경 가능할 때 인스턴스 변수에 대한 기본값을 정의하는 Python 방식입니다.
>>> class TestC():
... def __init__(self, attr=None):
... if attr is None:
... attr = []
... self.attr = attr
...
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]
내 첫 번째 코드 조각이 작동하는 이유는 변경 불가능한 유형을 사용하여 Python이 원할 때마다 새 인스턴스를 생성하기 때문입니다. 1을 1로 더해야한다면, 이전 1은 변경할 수 없기 때문에 Python은 새로운 2를 만듭니다. 그 이유는 대부분 해싱 때문이라고 생각합니다.
두 조각은 서로 다른 일을하므로 취향의 문제가 아니라 상황에서 올바른 행동이 무엇인지의 문제입니다. Python 문서 는 차이점을 설명하지만 다음은 몇 가지 예입니다.
전시 A를
class Foo:
def __init__(self):
self.num = 1
이것은 num
Foo 인스턴스에 바인딩 됩니다 . 이 필드에 대한 변경 사항은 다른 인스턴스로 전파되지 않습니다.
그러므로:
>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1
별첨 B
class Bar:
num = 1
이것은 num
Bar 클래스에 바인딩 됩니다 . 변경 사항이 전파됩니다!
>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3
실제 답변
클래스 변수가 필요하지 않고 인스턴스 변수에 대한 기본값 만 설정하면되는 경우 두 메서드 모두 똑같이 좋은가요? 아니면 그들 중 하나가 다른 것보다 더 '비단뱀 적'입니까?
전시회 B의 코드는 이에 대해 명백히 잘못되었습니다. 클래스 속성 (인스턴스 생성시 기본값)을 단일 인스턴스에 바인딩하려는 이유는 무엇입니까?
The code in exhibit A is okay.
If you want to give defaults for instance variables in your constructor I would however do this:
class Foo:
def __init__(num = None):
self.num = num if num is not None else 1
...or even:
class Foo:
DEFAULT_NUM = 1
def __init__(num = None):
self.num = num if num is not None else DEFAULT_NUM
...or even: (preferrable, but if and only if you are dealing with immutable types!)
class Foo:
def __init__(num = 1):
self.num = num
This way you can do:
foo1 = Foo(4)
foo2 = Foo() #use default
Using class members to give default values works very well just so long as you are careful only to do it with immutable values. If you try to do it with a list or a dict that would be pretty deadly. It also works where the instance attribute is a reference to a class just so long as the default value is None.
I've seen this technique used very successfully in repoze which is a framework that runs on top of Zope. The advantage here is not just that when your class is persisted to the database only the non-default attributes need to be saved, but also when you need to add a new field into the schema all the existing objects see the new field with its default value without any need to actually change the stored data.
I find it also works well in more general coding, but it's a style thing. Use whatever you are happiest with.
Using class members for default values of instance variables is not a good idea, and it's the first time I've seen this idea mentioned at all. It works in your example, but it may fail in a lot of cases. E.g., if the value is mutable, mutating it on an unmodified instance will alter the default:
>>> class c:
... l = []
...
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]
전파를 방지하는 클래스 변수를 None으로 선언 할 수도 있습니다. 이것은 잘 정의 된 클래스가 필요하고 AttributeErrors를 방지하고자 할 때 유용합니다. 예를 들면 :
>>> class TestClass(object):
... t = None
...
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>
또한 기본값이 필요한 경우 :
>>> class TestClassDefaults(object):
... t = None
... def __init__(self, t=None):
... self.t = t
...
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>
물론 변경 가능한 유형과 불변 유형을 기본값으로 사용하는 것에 대한 다른 답변의 정보를 따르십시오 __init__
.
'Development Tip' 카테고리의 다른 글
System.arraycopy가 Java에서 기본 제공되는 이유는 무엇입니까? (0) | 2020.10.12 |
---|---|
Ajax 업데이트 후 jQuery에서 이벤트 리 바인딩 (업데이트 패널) (0) | 2020.10.11 |
여러 요소 컬렉션을 결합하는 우아한 방법? (0) | 2020.10.11 |
정적 위치와 상대 위치의 차이점 (0) | 2020.10.11 |
판다의 데카르트 곱 (0) | 2020.10.11 |