@@ -226,27 +226,35 @@ def __init_subclass__(cls, *, family: object | None = None) -> None:
226226 cls ._family = family if family is not None else object ()
227227
228228 @typing .overload
229- def __init__ (self , arg : Histogram , / , * , metadata : Any = ...) -> None : ...
229+ def __init__ (
230+ self , arg : Histogram , / , * , metadata : Any = ..., __dict__ : Any = ...
231+ ) -> None : ...
230232
231233 @typing .overload
232- def __init__ (self , arg : dict [str , Any ], / , * , metadata : Any = ...) -> None : ...
234+ def __init__ (
235+ self , arg : dict [str , Any ], / , * , metadata : Any = ..., __dict__ : Any = ...
236+ ) -> None : ...
233237
234238 @typing .overload
235- def __init__ (self , arg : CppHistogram , / , * , metadata : Any = ...) -> None : ...
239+ def __init__ (
240+ self , arg : CppHistogram , / , * , metadata : Any = ..., __dict__ : Any = ...
241+ ) -> None : ...
236242
237243 @typing .overload
238244 def __init__ (
239245 self ,
240246 * axes : Axis | CppAxis ,
241247 storage : Storage = ...,
242248 metadata : Any = ...,
249+ __dict__ : Any = ...,
243250 ) -> None : ...
244251
245252 def __init__ (
246253 self ,
247254 * axes : Axis | CppAxis | Histogram | CppHistogram | dict [str , Any ],
248255 storage : Storage | None = None ,
249256 metadata : Any = NO_METADATA ,
257+ __dict__ : Any = None ,
250258 ) -> None :
251259 """
252260 Construct a new histogram.
@@ -263,33 +271,45 @@ def __init__(
263271 Select a storage to use in the histogram
264272 metadata : Any = None
265273 Data that is passed along if a new histogram is created. No not use
266- in new code; set properties in __dict__ directly instead.
274+ in new code; use ``__dict__`` instead.
275+ __dict__ : Any = None
276+ Better way to set metadata.
267277 """
268278 self ._variance_known = True
269279 storage_err_msg = "storage= is not allowed with conversion constructor"
270280
281+ if metadata is not NO_METADATA and __dict__ :
282+ msg = (
283+ "Can't set both metadata and __dict__. Set the 'metadata' key instead."
284+ )
285+ raise TypeError (msg )
286+ if metadata is not NO_METADATA :
287+ __dict__ = {"metadata" : metadata }
288+ if __dict__ is None :
289+ __dict__ = {}
290+
271291 # Allow construction from a raw histogram object (internal)
272292 if len (axes ) == 1 and isinstance (axes [0 ], tuple (_histograms )):
273293 if storage is not None :
274294 raise TypeError (storage_err_msg )
275295 cpp_hist : CppHistogram = axes [0 ] # type: ignore[assignment]
276- self ._from_histogram_cpp (cpp_hist , metadata = metadata )
296+ self ._from_histogram_cpp (cpp_hist , __dict__ = __dict__ )
277297 return
278298
279299 # If we construct with another Histogram as the only positional argument,
280300 # support that too
281301 if len (axes ) == 1 and isinstance (axes [0 ], Histogram ):
282302 if storage is not None :
283303 raise TypeError (storage_err_msg )
284- self ._from_histogram_object (axes [0 ], metadata = metadata )
304+ self ._from_histogram_object (axes [0 ], __dict__ = __dict__ )
285305 return
286306
287307 # Support objects that provide a to_boost method, like Uproot
288308 if len (axes ) == 1 and hasattr (axes [0 ], "_to_boost_histogram_" ):
289309 if storage is not None :
290310 raise TypeError (storage_err_msg )
291311 self ._from_histogram_object (
292- axes [0 ]._to_boost_histogram_ (), metadata = metadata
312+ axes [0 ]._to_boost_histogram_ (), __dict__ = __dict__
293313 )
294314 return
295315
@@ -298,15 +318,14 @@ def __init__(
298318 if storage is not None :
299319 raise TypeError (storage_err_msg )
300320 self ._from_histogram_object (
301- serialization .from_uhi (axes [0 ]), metadata = metadata
321+ serialization .from_uhi (axes [0 ]), __dict__ = __dict__
302322 )
303323 return
304324
305325 if storage is None :
306326 storage = Double ()
307327
308- if metadata is not NO_METADATA :
309- self .metadata = metadata
328+ self .__dict__ .update (__dict__ )
310329
311330 # Check for missed parenthesis or incorrect types
312331 if not isinstance (storage , Storage ):
@@ -347,7 +366,7 @@ def _clone(
347366
348367 self = cls .__new__ (cls )
349368 if isinstance (_hist , tuple (_histograms )):
350- self ._from_histogram_cpp (_hist , metadata = NO_METADATA ) # type: ignore[arg-type]
369+ self ._from_histogram_cpp (_hist , __dict__ = {} ) # type: ignore[arg-type]
351370 if other is not None :
352371 return cls ._clone (self , other = other , memo = memo )
353372 return self
@@ -357,18 +376,19 @@ def _clone(
357376 if other is None :
358377 other = _hist
359378
360- self ._from_histogram_object (_hist , metadata = NO_METADATA )
361-
362379 if memo is NOTHING :
363- self . __dict__ = copy .copy (other .__dict__ )
380+ dict_copy = copy .copy (other .__dict__ )
364381 else :
365- self .__dict__ = copy .deepcopy (other .__dict__ , memo )
382+ dict_copy = copy .deepcopy (other .__dict__ , memo )
383+
384+ self ._from_histogram_object (_hist , __dict__ = dict_copy )
366385
367386 for ax in self .axes :
368387 if memo is NOTHING :
369- ax .__dict__ = copy .copy (ax ._ax .raw_metadata )
388+ ax ._ax . raw_metadata = copy .copy (ax ._ax .raw_metadata )
370389 else :
371- ax .__dict__ = copy .deepcopy (ax ._ax .raw_metadata , memo )
390+ ax ._ax .raw_metadata = copy .deepcopy (ax ._ax .raw_metadata , memo )
391+ ax .__dict__ = ax ._ax .raw_metadata
372392 return self
373393
374394 def _new_hist (self , _hist : CppHistogram , memo : Any = NOTHING ) -> Self :
@@ -377,17 +397,20 @@ def _new_hist(self, _hist: CppHistogram, memo: Any = NOTHING) -> Self:
377397 """
378398 return self .__class__ ._clone (_hist , other = self , memo = memo )
379399
380- def _from_histogram_cpp (self , other : CppHistogram , * , metadata : Any ) -> None :
400+ def _from_histogram_cpp (
401+ self , other : CppHistogram , * , __dict__ : dict [str , Any ]
402+ ) -> None :
381403 """
382404 Import a Cpp histogram.
383405 """
384406 self ._variance_known = True
385407 self ._hist = other
386- if metadata is not NO_METADATA :
387- self .metadata = metadata
408+ self .__dict__ .update (__dict__ )
388409 self .axes = self ._generate_axes_ ()
389410
390- def _from_histogram_object (self , other : Histogram , * , metadata : Any ) -> None :
411+ def _from_histogram_object (
412+ self , other : Histogram , * , __dict__ : dict [str , Any ]
413+ ) -> None :
391414 """
392415 Convert self into a new histogram object based on another, possibly
393416 converting from a different subclass.
@@ -396,11 +419,9 @@ def _from_histogram_object(self, other: Histogram, *, metadata: Any) -> None:
396419 self .__dict__ = copy .copy (other .__dict__ )
397420 self .axes = self ._generate_axes_ ()
398421 for ax in self .axes :
399- ax .__dict__ = copy .copy (ax ._ax .raw_metadata )
400- if metadata is not NO_METADATA :
401- self .metadata = metadata
402- elif "metadata" in other .__dict__ :
403- self .metadata = other .metadata
422+ ax .__dict__ .update (ax ._ax .raw_metadata )
423+ self .__dict__ .update (other .__dict__ )
424+ self .__dict__ .update (__dict__ )
404425
405426 # Allow custom behavior on either "from" or "to"
406427 other ._export_bh_ (self )
0 commit comments