33import itertools
44import operator
55import re
6- from typing import Dict , List , Optional , Sequence , Tuple , Union
6+ from typing import Dict , List , Optional , Sequence , Tuple , TypeVar , Union
77
88import numpy as np
99
1010from pandas ._libs import Timedelta , Timestamp , internals as libinternals , lib
11- from pandas ._typing import DtypeObj , Label
11+ from pandas ._typing import ArrayLike , DtypeObj , Label
1212from pandas .util ._validators import validate_bool_kwarg
1313
1414from pandas .core .dtypes .cast import (
5858
5959# TODO: flexible with index=None and/or items=None
6060
61+ T = TypeVar ("T" , bound = "BlockManager" )
62+
6163
6264class BlockManager (PandasObject ):
6365 """
@@ -149,6 +151,13 @@ def __init__(
149151 self ._blknos = None
150152 self ._blklocs = None
151153
154+ @classmethod
155+ def from_blocks (cls , blocks : List [Block ], axes : List [Index ]):
156+ """
157+ Constructor for BlockManager and SingleBlockManager with same signature.
158+ """
159+ return cls (blocks , axes , do_integrity_check = False )
160+
152161 @property
153162 def blknos (self ):
154163 """
@@ -176,18 +185,19 @@ def blklocs(self):
176185
177186 return self ._blklocs
178187
179- def make_empty (self , axes = None ) -> "BlockManager" :
188+ def make_empty (self : T , axes = None ) -> T :
180189 """ return an empty BlockManager with the items axis of len 0 """
181190 if axes is None :
182191 axes = [Index ([])] + self .axes [1 :]
183192
184193 # preserve dtype if possible
185194 if self .ndim == 1 :
186195 assert isinstance (self , SingleBlockManager ) # for mypy
187- blocks = np .array ([], dtype = self .array_dtype )
196+ arr = np .array ([], dtype = self .array_dtype )
197+ blocks = [make_block (arr , placement = slice (0 , 0 ), ndim = 1 )]
188198 else :
189199 blocks = []
190- return type (self )(blocks , axes )
200+ return type (self ). from_blocks (blocks , axes )
191201
192202 def __nonzero__ (self ) -> bool :
193203 return True
@@ -380,7 +390,7 @@ def reduce(self, func, *args, **kwargs):
380390
381391 return res
382392
383- def apply (self , f , filter = None , ** kwargs ) -> "BlockManager" :
393+ def apply (self : T , f , filter = None , ** kwargs ) -> T :
384394 """
385395 Iterate over the blocks, collect and create a new BlockManager.
386396
@@ -458,8 +468,8 @@ def apply(self, f, filter=None, **kwargs) -> "BlockManager":
458468
459469 if len (result_blocks ) == 0 :
460470 return self .make_empty (self .axes )
461- bm = type ( self )( result_blocks , self . axes , do_integrity_check = False )
462- return bm
471+
472+ return type ( self ). from_blocks ( result_blocks , self . axes )
463473
464474 def quantile (
465475 self ,
@@ -658,7 +668,7 @@ def comp(s, regex=False):
658668 rb = new_rb
659669 result_blocks .extend (rb )
660670
661- bm = type (self )(result_blocks , self .axes )
671+ bm = type (self ). from_blocks (result_blocks , self .axes )
662672 bm ._consolidate_inplace ()
663673 return bm
664674
@@ -747,7 +757,7 @@ def combine(self, blocks: List[Block], copy: bool = True) -> "BlockManager":
747757 axes = list (self .axes )
748758 axes [0 ] = self .items .take (indexer )
749759
750- return type (self )(new_blocks , axes , do_integrity_check = False )
760+ return type (self ). from_blocks (new_blocks , axes )
751761
752762 def get_slice (self , slobj : slice , axis : int = 0 ) -> "BlockManager" :
753763
@@ -774,7 +784,7 @@ def __contains__(self, item) -> bool:
774784 def nblocks (self ) -> int :
775785 return len (self .blocks )
776786
777- def copy (self , deep = True ) -> "BlockManager" :
787+ def copy (self : T , deep = True ) -> T :
778788 """
779789 Make deep or shallow copy of BlockManager
780790
@@ -1244,14 +1254,14 @@ def reindex_axis(
12441254 )
12451255
12461256 def reindex_indexer (
1247- self ,
1257+ self : T ,
12481258 new_axis ,
12491259 indexer ,
12501260 axis : int ,
12511261 fill_value = None ,
1252- allow_dups = False ,
1262+ allow_dups : bool = False ,
12531263 copy : bool = True ,
1254- ):
1264+ ) -> T :
12551265 """
12561266 Parameters
12571267 ----------
@@ -1299,7 +1309,8 @@ def reindex_indexer(
12991309
13001310 new_axes = list (self .axes )
13011311 new_axes [axis ] = new_axis
1302- return type (self )(new_blocks , new_axes )
1312+
1313+ return type (self ).from_blocks (new_blocks , new_axes )
13031314
13041315 def _slice_take_blocks_ax0 (self , slice_or_indexer , fill_tuple = None ):
13051316 """
@@ -1500,6 +1511,8 @@ def __init__(
15001511 do_integrity_check : bool = False ,
15011512 fastpath : bool = False ,
15021513 ):
1514+ assert isinstance (block , Block ), type (block )
1515+
15031516 if isinstance (axis , list ):
15041517 if len (axis ) != 1 :
15051518 raise ValueError (
@@ -1510,38 +1523,29 @@ def __init__(
15101523 # passed from constructor, single block, single axis
15111524 if fastpath :
15121525 self .axes = [axis ]
1513- if isinstance (block , list ):
1514-
1515- # empty block
1516- if len (block ) == 0 :
1517- block = [np .array ([])]
1518- elif len (block ) != 1 :
1519- raise ValueError (
1520- "Cannot create SingleBlockManager with more than 1 block"
1521- )
1522- block = block [0 ]
15231526 else :
15241527 self .axes = [ensure_index (axis )]
15251528
1526- # create the block here
1527- if isinstance (block , list ):
1528-
1529- # provide consolidation to the interleaved_dtype
1530- if len (block ) > 1 :
1531- dtype = _interleaved_dtype (block )
1532- block = [b .astype (dtype ) for b in block ]
1533- block = _consolidate (block )
1534-
1535- if len (block ) != 1 :
1536- raise ValueError (
1537- "Cannot create SingleBlockManager with more than 1 block"
1538- )
1539- block = block [0 ]
1529+ self .blocks = tuple ([block ])
15401530
1541- if not isinstance (block , Block ):
1542- block = make_block (block , placement = slice (0 , len (axis )), ndim = 1 )
1531+ @classmethod
1532+ def from_blocks (
1533+ cls , blocks : List [Block ], axes : List [Index ]
1534+ ) -> "SingleBlockManager" :
1535+ """
1536+ Constructor for BlockManager and SingleBlockManager with same signature.
1537+ """
1538+ assert len (blocks ) == 1
1539+ assert len (axes ) == 1
1540+ return cls (blocks [0 ], axes [0 ], do_integrity_check = False , fastpath = True )
15431541
1544- self .blocks = tuple ([block ])
1542+ @classmethod
1543+ def from_array (cls , array : ArrayLike , index : Index ) -> "SingleBlockManager" :
1544+ """
1545+ Constructor for if we have an array that is not yet a Block.
1546+ """
1547+ block = make_block (array , placement = slice (0 , len (index )), ndim = 1 )
1548+ return cls (block , index , fastpath = True )
15451549
15461550 def _post_setstate (self ):
15471551 pass
@@ -1568,7 +1572,10 @@ def get_slice(self, slobj: slice, axis: int = 0) -> "SingleBlockManager":
15681572 if axis >= self .ndim :
15691573 raise IndexError ("Requested axis not found in manager" )
15701574
1571- return type (self )(self ._block ._slice (slobj ), self .index [slobj ], fastpath = True )
1575+ blk = self ._block
1576+ array = blk ._slice (slobj )
1577+ block = blk .make_block_same_class (array , placement = range (len (array )))
1578+ return type (self )(block , self .index [slobj ], fastpath = True )
15721579
15731580 @property
15741581 def index (self ) -> Index :
@@ -1626,7 +1633,7 @@ def fast_xs(self, loc):
16261633 """
16271634 raise NotImplementedError ("Use series._values[loc] instead" )
16281635
1629- def concat (self , to_concat , new_axis ) -> "SingleBlockManager" :
1636+ def concat (self , to_concat , new_axis : Index ) -> "SingleBlockManager" :
16301637 """
16311638 Concatenate a list of SingleBlockManagers into a single
16321639 SingleBlockManager.
0 commit comments