중첩된 딕트의 Python 데이터 클래스
3.7의 표준 라이브러리는 데이터 클래스를 딕트로 재귀적으로 변환할 수 있습니다(예: 문서에서).
from dataclasses import dataclass, asdict
from typing import List
@dataclass
class Point:
x: int
y: int
@dataclass
class C:
mylist: List[Point]
p = Point(10, 20)
assert asdict(p) == {'x': 10, 'y': 20}
c = C([Point(0, 0), Point(10, 4)])
tmp = {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
assert asdict(c) == tmp
중첩이 있을 때 딕트를 다시 데이터 클래스로 전환하는 방법을 찾고 있습니다. 같은 거.C(**tmp)
데이터 클래스의 필드가 데이터 클래스가 아닌 단순 유형인 경우에만 작동합니다.저는 jsonpickle에 대해 잘 알고 있습니다. 하지만 그것은 눈에 띄는 보안 경고와 함께 나옵니다.
편집:
다음과 같은 라이브러리가 제시되었습니다.
- 데이카이트
- 마슈마로 (잠시 사용했는데, 잘 작동하지만 금방 까다로운 코너 케이스에 부딪혔습니다.)
- pydantic (매우 잘 작동하고, 우수한 문서화 및 더 적은 코너 케이스)
저는 사전에서 데이터 클래스를 쉽게 만들 수 있는 도구인 의 저자입니다.
에는 기능이 .from_dict
다음은 빠른 사용 예입니다.
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class User:
name: str
age: int
is_active: bool
data = {
'name': 'john',
'age': 30,
'is_active': True,
}
user = from_dict(data_class=User, data=data)
assert user == User(name='john', age=30, is_active=True)
게가다.dacite
에서는 다음합니다.
- 중첩된 구조물
- (기본) 유형 검사
- 선택적 필드(즉, 입력).선택사항)
- 노동조합.
- 소장품
- 주조 및 변환의 가치
- 필드 이름 재매핑
그리고 그것은 잘 테스트되었습니다 - 100% 코드 적용 범위!
dacite를 설치하려면 pip(또는 pipenv)을 사용하면 됩니다.
$ pip install dacite
입니다.asdict
구체적으로 함수 또는구으로내, 재도우기미능귀부적체▁or▁the._asdict_inner
다음을 사용합니다.
# Source: https://github.com/python/cpython/blob/master/Lib/dataclasses.py
def _asdict_inner(obj, dict_factory):
if _is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _asdict_inner(getattr(obj, f.name), dict_factory)
result.append((f.name, value))
return dict_factory(result)
elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
# [large block of author comments]
return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj])
elif isinstance(obj, (list, tuple)):
# [ditto]
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
elif isinstance(obj, dict):
return type(obj)((_asdict_inner(k, dict_factory),
_asdict_inner(v, dict_factory))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
asdict
어떤, 단히몇가주위로부를르고으장지순,부▁simply▁the단고르위▁calls▁with,dict_factory=dict
결석으로
설명에 언급된 대로 필요한 형식 태그를 사용하여 출력 사전을 만드는 방법은 무엇입니까?
유형 정보 추가
사용자 지정 반환 래퍼를 만드는 작업을 수행했습니다.dict
:
class TypeDict(dict):
def __init__(self, t, *args, **kwargs):
super(TypeDict, self).__init__(*args, **kwargs)
if not isinstance(t, type):
raise TypeError("t must be a type")
self._type = t
@property
def type(self):
return self._type
원래 코드를 보면, 다른 절들은 다음의 컨테이너만 다루기 때문에, 첫 번째 절만 이 래퍼를 사용하도록 수정하면 됩니다.dataclass
-es:
# only use dict for now; easy to add back later
def _todict_inner(obj):
if is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _todict_inner(getattr(obj, f.name))
result.append((f.name, value))
return TypeDict(type(obj), result)
elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
return type(obj)(*[_todict_inner(v) for v in obj])
elif isinstance(obj, (list, tuple)):
return type(obj)(_todict_inner(v) for v in obj)
elif isinstance(obj, dict):
return type(obj)((_todict_inner(k), _todict_inner(v))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
가져오기:
from dataclasses import dataclass, fields, is_dataclass
# thanks to Patrick Haugh
from typing import *
# deepcopy
import copy
사용된 기능:
# copy of the internal function _is_dataclass_instance
def is_dataclass_instance(obj):
return is_dataclass(obj) and not is_dataclass(obj.type)
# the adapted version of asdict
def todict(obj):
if not is_dataclass_instance(obj):
raise TypeError("todict() should be called on dataclass instances")
return _todict_inner(obj)
예제 데이터 클래스를 사용하여 테스트:
c = C([Point(0, 0), Point(10, 4)])
print(c)
cd = todict(c)
print(cd)
# {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
print(cd.type)
# <class '__main__.C'>
결과는 예상대로입니다.
로다변으로 dataclass
에서 사용하는 재귀 루틴입니다.asdict
일부 비교적 사소한 변경 사항과 함께 역방향 프로세스에 다시 사용할 수 있습니다.
def _fromdict_inner(obj):
# reconstruct the dataclass using the type tag
if is_dataclass_dict(obj):
result = {}
for name, data in obj.items():
result[name] = _fromdict_inner(data)
return obj.type(**result)
# exactly the same as before (without the tuple clause)
elif isinstance(obj, (list, tuple)):
return type(obj)(_fromdict_inner(v) for v in obj)
elif isinstance(obj, dict):
return type(obj)((_fromdict_inner(k), _fromdict_inner(v))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
사용된 기능:
def is_dataclass_dict(obj):
return isinstance(obj, TypeDict)
def fromdict(obj):
if not is_dataclass_dict(obj):
raise TypeError("fromdict() should be called on TypeDict instances")
return _fromdict_inner(obj)
테스트:
c = C([Point(0, 0), Point(10, 4)])
cd = todict(c)
cf = fromdict(cd)
print(c)
# C(mylist=[Point(x=0, y=0), Point(x=10, y=4)])
print(cf)
# C(mylist=[Point(x=0, y=0), Point(x=10, y=4)])
역시.
다섯 개의 선만 있으면 됩니다.
def dataclass_from_dict(klass, d):
try:
fieldtypes = {f.name:f.type for f in dataclasses.fields(klass)}
return klass(**{f:dataclass_from_dict(fieldtypes[f],d[f]) for f in d})
except:
return d # Not a dataclass field
샘플 사용량:
from dataclasses import dataclass, asdict
@dataclass
class Point:
x: float
y: float
@dataclass
class Line:
a: Point
b: Point
line = Line(Point(1,2), Point(3,4))
assert line == dataclass_from_dict(Line, asdict(line))
json을 포함한 전체 코드는 https://gist.github.com/gatopeich/1efd3e1e4269e1e98fae9983bb914f22 에서 확인할 수 있습니다.
추가 모듈을 사용하지 않고 자동으로 변환하는 기능을 사용할 수 있습니다.dict
값을 올바른 형식으로 입력합니다.이 함수는 다음과 같이 호출됩니다.__init__
.
from dataclasses import dataclass, asdict
@dataclass
class Bar:
fee: str
far: str
@dataclass
class Foo:
bar: Bar
def __post_init__(self):
if isinstance(self.bar, dict):
self.bar = Bar(**self.bar)
foo = Foo(bar=Bar(fee="La", far="So"))
d= asdict(foo)
print(d) # {'bar': {'fee': 'La', 'far': 'So'}}
o = Foo(**d)
print(o) # Foo(bar=Bar(fee='La', far='So'))
이 솔루션에는 데이터 클래스가 아닌 개체를 사용할 수 있다는 추가적인 이점이 있습니다.그것이 있는 한str
기능을 다시 변환할 수 있습니다. 공정한 게임입니다.예를 들어, 다음을 유지하는 데 사용할 수 있습니다.str
를 의필드로 IP4Address
내부적으로
구성표에 따라 딕트에서 데이터 클래스 개체를 만드는 데 mashumaro를 사용할 수 있습니다.이 라이브러리의 믹스인은 편리함을 더해줍니다.from_dict
그리고.to_dict
데이터 클래스에 대한 메서드:
from dataclasses import dataclass
from typing import List
from mashumaro import DataClassDictMixin
@dataclass
class Point(DataClassDictMixin):
x: int
y: int
@dataclass
class C(DataClassDictMixin):
mylist: List[Point]
p = Point(10, 20)
tmp = {'x': 10, 'y': 20}
assert p.to_dict() == tmp
assert Point.from_dict(tmp) == p
c = C([Point(0, 0), Point(10, 4)])
tmp = {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
assert c.to_dict() == tmp
assert C.from_dict(tmp) == c
제가 아직 언급하지 않은 가능한 해결책은 사용하는 것입니다.이 라이브러리는 다음의 변환을 제공합니다.dataclass
대한와 JSON에 대한 인스턴스, JSON에 대한 (JSON에 대한 인스턴스)dict
(및 이전 답변에서 제안된 것과 같습니다.)
dataclasses-json
을 스를로장합니다야해식클로 꾸며야 합니다.@dataclass_json
에 @dataclass
장식된 하거나 JSON에서하거나 JSON으로 몇 .dict
:
from_dict(...)
from_json(...)
to_dict(...)
to_json(...)
다음은 문제의 원래 코드를 약간 수정한 버전입니다.요▁the다▁required를 추가했습니다.@dataclass_json
와 장식가assert
는 환을위한에서 됩니다.dict
를 Point
그리고.C
:
from dataclasses import dataclass, asdict
from dataclasses_json import dataclass_json
from typing import List
@dataclass_json
@dataclass
class Point:
x: int
y: int
@dataclass_json
@dataclass
class C:
mylist: List[Point]
p = Point(10, 20)
assert asdict(p) == {'x': 10, 'y': 20}
assert p == Point.from_dict({'x': 10, 'y': 20})
c = C([Point(0, 0), Point(10, 4)])
tmp = {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
assert asdict(c) == tmp
assert c == C.from_dict(tmp)
사전 정의된 기존 데이터 클래스에서 또는 데이터 클래스로 JSON을 생성하는 것이 목표라면 사용자 지정 인코더 및 디코더 후크를 작성하기만 하면 됩니다.사용 안 함dataclasses.asdict()
대신 JSON에 원래 데이터 클래스에 대한 (안전한) 참조를 기록합니다.
jsonpickle
임의의 Python 개체에 대한 참조를 저장하고 해당 생성자에게 데이터를 전달하므로 안전하지 않습니다.이러한 참조를 통해 jsonpickle을 통해 내부 Python 데이터 구조를 참조하고 함수, 클래스 및 모듈을 마음대로 만들고 실행할 수 있습니다.하지만 그렇다고 해서 그러한 참조를 안전하게 처리할 수 없다는 뜻은 아닙니다.호출하지 않고 가져오기만 하고 개체가 실제 데이터 클래스 유형인지 확인한 다음 개체를 사용합니다.
프레임워크는 충분히 일반적으로 만들 수 있지만 여전히 JSON 직렬 사용 가능 유형과 기반 인스턴스로만 제한됩니다.
import dataclasses
import importlib
import sys
def dataclass_object_dump(ob):
datacls = type(ob)
if not dataclasses.is_dataclass(datacls):
raise TypeError(f"Expected dataclass instance, got '{datacls!r}' object")
mod = sys.modules.get(datacls.__module__)
if mod is None or not hasattr(mod, datacls.__qualname__):
raise ValueError(f"Can't resolve '{datacls!r}' reference")
ref = f"{datacls.__module__}.{datacls.__qualname__}"
fields = (f.name for f in dataclasses.fields(ob))
return {**{f: getattr(ob, f) for f in fields}, '__dataclass__': ref}
def dataclass_object_load(d):
ref = d.pop('__dataclass__', None)
if ref is None:
return d
try:
modname, hasdot, qualname = ref.rpartition('.')
module = importlib.import_module(modname)
datacls = getattr(module, qualname)
if not dataclasses.is_dataclass(datacls) or not isinstance(datacls, type):
raise ValueError
return datacls(**d)
except (ModuleNotFoundError, ValueError, AttributeError, TypeError):
raise ValueError(f"Invalid dataclass reference {ref!r}") from None
JSON-RPC 스타일 클래스 힌트를 사용하여 데이터 클래스의 이름을 지정하고 로드 시에도 동일한 필드를 가진 데이터 클래스임이 확인됩니다.필드의 값에 대한 유형 검사는 수행되지 않습니다(그것은 완전히 다른 주전자의 물고기이기 때문입니다).
이것들을 사용합니다.default
그리고.object_hook
입니다.json.dump[s]()
그리고.json.dump[s]()
:
>>> print(json.dumps(c, default=dataclass_object_dump, indent=4))
{
"mylist": [
{
"x": 0,
"y": 0,
"__dataclass__": "__main__.Point"
},
{
"x": 10,
"y": 4,
"__dataclass__": "__main__.Point"
}
],
"__dataclass__": "__main__.C"
}
>>> json.loads(json.dumps(c, default=dataclass_object_dump), object_hook=dataclass_object_load)
C(mylist=[Point(x=0, y=0), Point(x=10, y=4)])
>>> json.loads(json.dumps(c, default=dataclass_object_dump), object_hook=dataclass_object_load) == c
True
또는 동일한 후크를 사용하여 및 클래스의 인스턴스를 만듭니다.
완전 수식 모듈 및 클래스 이름을 사용하는 대신 별도의 레지스트리를 사용하여 허용되는 유형 이름을 매핑할 수도 있습니다. 인코딩 시 레지스트리를 확인하고 디코딩할 때 다시 확인하여 개발 중인 데이터 클래스를 등록하는 것을 잊지 않도록 할 수도 있습니다.
저는 지금쯤이면 JSON 시리즈화 라이브러리가 엄청나게 있을 것이라는 것을 알고 있습니다. 솔직히 말해서 이 기사를 조금 늦게 발견했을 수도 있습니다.그러나 사용 가능한 최신(그리고 잘 테스트된) 옵션은 라이브러리입니다.이는 최근(어떤 경우든 2주 전 기준) v0.18.0 릴리스의 운영/안정 상태로 전환되었습니다.
그것은 타이핑 제네릭을 꽤 견고하게 지원합니다.typing
뿐만 모뿐만아데클이사니의 데이터 Union
유형 및 패턴이 지정된 날짜와 시간.자동 키 케이싱 변환(예: 낙타에서 뱀으로) 및 암시적 유형 주조(예: 주석이 달린 문자열)와 같이 개인적으로 매우 유용하다고 생각한 다른 기능int
도 구현됩니다.
를 사용하는 것이 가장 좋습니다.JSONWizard
Mixin 클래스는 다음과 같은 유용한 클래스 메서드를 제공합니다.
from_json
from_dict
/from_list
to_dict
to_json
/list_to_json
다음은 Python 3.7+에서 테스트된 매우 자기 설명적인 사용법입니다.__future__
가져오기:
from __future__ import annotations
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass
class C(JSONWizard):
my_list: list[Point]
@dataclass
class Point(JSONWizard):
x: int
y: int
# Serialize Point instance
p = Point(10, 20)
tmp = {'x': 10, 'y': 20}
assert p.to_dict() == tmp
assert Point.from_dict(tmp) == p
c = C([Point(0, 0), Point(10, 4)])
# default case transform is 'camelCase', though this can be overridden
# with a custom Meta config supplied for the main dataclass.
tmp = {'myList': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
assert c.to_dict() == tmp
assert C.from_dict(tmp) == c
NB: 기술적으로 메인 데이터 클래스(즉, 직렬화 중인 모델)만 하위 클래스로 분류하면 됩니다. 원하는 경우 중첩된 데이터 클래스를 그대로 둘 수 있습니다.
클래스 상속 모델을 모두 사용하지 않는 경우 다른 옵션은 다음과 같은 내보낸 도우미 함수를 사용하는 것입니다.fromdict
,asdict
를 Python에서 Python으로에서 변환합니다.dict
필요에 따라 개체를 선택합니다.
목록도 지원하는 간단한 솔루션(기타 일반 용도로 확장 가능)
from dataclasses import dataclass, asdict, fields, is_dataclass
from typing import List
from types import GenericAlias
def asdataclass(klass, d):
if not is_dataclass(klass):
return d
values = {}
for f in fields(klass):
if isinstance(f.type, GenericAlias) and f.type.__origin__ == list:
values[f.name] = [asdataclass(f.type.__args__[0], d2) for d2 in d[f.name]]
else:
values[f.name] = asdataclass(f.type,d[f.name])
return klass(**values)
@dataclass
class Point:
x: int
y: int
@dataclass
class C:
mylist: list[Point]
title: str = ""
c = C([Point(0, 0), Point(10, 4)])
assert c == asdataclass(C, asdict(c))
https://stackoverflow.com/a/54769644/871166 을 기반으로 합니다.
저는 정말로 이 대답에서 gatopeich가 제시한 개념이 이 질문에 대한 최선의 접근법이라고 생각합니다.
제가 그의 코드를 수정하고 교화했습니다.다음은 사전에서 데이터 클래스를 다시 로드하는 올바른 기능입니다.
def dataclass_from_dict(cls: type, src: t.Mapping[str, t.Any]) -> t.Any:
field_types_lookup = {
field.name: field.type
for field in dataclasses.fields(cls)
}
constructor_inputs = {}
for field_name, value in src.items():
try:
constructor_inputs[field_name] = dataclass_from_dict(field_types_lookup[field_name], value)
except TypeError as e:
# type error from fields() call in recursive call
# indicates that field is not a dataclass, this is how we are
# breaking the recursion. If not a dataclass - no need for loading
constructor_inputs[field_name] = value
except KeyError:
# similar, field not defined on dataclass, pass as plain field value
constructor_inputs[field_name] = value
return cls(**constructor_inputs)
그런 다음 다음을 사용하여 테스트할 수 있습니다.
@dataclass
class Point:
x: float
y: float
@dataclass
class Line:
a: Point
b: Point
p1, p2 = Point(1,1), Point(2,2)
line = Line(p1, p1)
assert line == dataclass_from_dict(Line, asdict(line))
from dataclasses import dataclass, is_dataclass
@dataclass
class test2:
a: str = 'name'
b: int = 222
@dataclass
class test:
a: str = 'name'
b: int = 222
t: test2 = None
a = test(a = 2222222222, t=test2(a="ssss"))
print(a)
def dataclass_from_dict(schema: any, data: dict):
data_updated = {
key: (
data[key]
if not is_dataclass(schema.__annotations__[key])
else dataclass_from_dict(schema.__annotations__[key], data[key])
)
for key in data.keys()
}
return schema(**data_updated)
print(dataclass_from_dict(test, {'a': 1111111, 't': {'a': 'nazwa'} }))
가능한 대안은 경량 칠리 라이브러리일 수 있습니다.
from dataclasses import dataclass, asdict
from typing import List
from chili import init_dataclass
@dataclass
class Point:
x: int
y: int
@dataclass
class C:
mylist: List[Point]
p = Point(10, 20)
assert asdict(p) == {'x': 10, 'y': 20}
c = C([Point(0, 0), Point(10, 4)])
tmp = {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
assert asdict(c) == tmp
assert c == init_dataclass(tmp, C)
Chili는 사용자 지정 일반 유형을 포함하여 거의 전체 타이핑 모듈을 지원합니다.자세한 내용은 여기에서 확인할 수 있습니다. https://github.com/kodemore/chili
설치는 다음을 실행하는 것만으로 pip 또는 시를 통해 수행할 수 있습니다.
pip install chili
또는
poetry add chili
종속성은 하나뿐입니다. 바로 확장자를 입력하는 것입니다.
undictify는 도움이 될 수 있는 도서관입니다.다음은 최소 사용 예입니다.
import json
from dataclasses import dataclass
from typing import List, NamedTuple, Optional, Any
from undictify import type_checked_constructor
@type_checked_constructor(skip=True)
@dataclass
class Heart:
weight_in_kg: float
pulse_at_rest: int
@type_checked_constructor(skip=True)
@dataclass
class Human:
id: int
name: str
nick: Optional[str]
heart: Heart
friend_ids: List[int]
tobias_dict = json.loads('''
{
"id": 1,
"name": "Tobias",
"heart": {
"weight_in_kg": 0.31,
"pulse_at_rest": 52
},
"friend_ids": [2, 3, 4, 5]
}''')
tobias = Human(**tobias_dict)
Validobj는 바로 그것을 합니다.다른 라이브러리에 비해 간단한 인터페이스(현재는 하나의 기능만 제공)를 제공하고 정보 오류 메시지를 강조합니다.예를 들어, 다음과 같은 스키마가 주어지면
import dataclasses
from typing import Optional, List
@dataclasses.dataclass
class User:
name: str
phone: Optional[str] = None
tasks: List[str] = dataclasses.field(default_factory=list)
다음과 같은 오류가 발생합니다.
>>> import validobj
>>> validobj.parse_input({
... 'phone': '555-1337-000', 'address': 'Somewhereville', 'nme': 'Zahari'}, User
... )
Traceback (most recent call last):
...
WrongKeysError: Cannot process value into 'User' because fields do not match.
The following required keys are missing: {'name'}. The following keys are unknown: {'nme', 'address'}.
Alternatives to invalid value 'nme' include:
- name
All valid options are:
- name
- phone
- tasks
특정 필드의 오타에 대해
이 문제를 해결하기 위해 복합 패턴을 사용하는 것을 제안하고 싶습니다. 주요 장점은 이 패턴에 클래스를 계속 추가하고 동일한 방식으로 동작하도록 할 수 있다는 것입니다.
from dataclasses import dataclass
from typing import List
@dataclass
class CompositeDict:
def as_dict(self):
retval = dict()
for key, value in self.__dict__.items():
if key in self.__dataclass_fields__.keys():
if type(value) is list:
retval[key] = [item.as_dict() for item in value]
else:
retval[key] = value
return retval
@dataclass
class Point(CompositeDict):
x: int
y: int
@dataclass
class C(CompositeDict):
mylist: List[Point]
c = C([Point(0, 0), Point(10, 4)])
tmp = {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
assert c.as_dict() == tmp
참고로 CompositeDict 클래스 내에서 중첩된 딕트, 튜플 등과 같은 다른 사례를 처리하는 공장 패턴을 사용하여 보일러 플레이트를 많이 절약할 수 있습니다.
from validated_dc import ValidatedDC
from dataclasses import dataclass
from typing import List, Union
@dataclass
class Foo(ValidatedDC):
foo: int
@dataclass
class Bar(ValidatedDC):
bar: Union[Foo, List[Foo]]
foo = {'foo': 1}
instance = Bar(bar=foo)
print(instance.get_errors()) # None
print(instance) # Bar(bar=Foo(foo=1))
list_foo = [{'foo': 1}, {'foo': 2}]
instance = Bar(bar=list_foo)
print(instance.get_errors()) # None
print(instance) # Bar(bar=[Foo(foo=1), Foo(foo=2)])
validated_dc:
https://github.com/EvgeniyBurdin/://github.com/EvgeniyBurdin/validated_dc
자세한 예는 다음과 같습니다.
https://github.com//validated_dc/blob/master/examples/detailed.pyhttps ://github.com/EvgeniyBurdin/validated_dc/blob/master/examples/detailed.py
언급URL : https://stackoverflow.com/questions/53376099/python-dataclass-from-a-nested-dict
'programing' 카테고리의 다른 글
'사용' 지시어는 C#에서 네임스페이스 내부에 있어야 합니까 아니면 외부에 있어야 합니까? (0) | 2023.05.02 |
---|---|
여러 개의 키로 "구별"을 효율적으로 수행하는 방법은 무엇입니까? (0) | 2023.05.02 |
Excel에서 GUID를 만드는 방법은 무엇입니까? (0) | 2023.05.02 |
SQL 서버 백업을 Azure SQL 데이터베이스로 복원 (0) | 2023.05.02 |
다차원 배열에서 Ubound를 사용한 VBA (0) | 2023.05.02 |