Saya mencoba untuk membuat subkelas pd.DataFrame
yang memiliki dua argumen yang diperlukan ketika menginisialisasi ( group
dan timestamp_col
). Saya ingin menjalankan validasi pada argumen itu group
dan timestamp_col
, jadi saya punya metode penyetel untuk masing-masing properti. Ini semua bekerja sampai saya coba set_index()
dan dapatkan TypeError: 'NoneType' object is not iterable
. Tampaknya tidak ada argumen yang diteruskan ke fungsi setter saya di test_set_index
dan test_assignment_with_indexed_obj
. Jika saya menambah if g == None: return
fungsi setter saya, saya bisa lulus ujian tetapi tidak berpikir itu adalah solusi yang tepat.
Bagaimana saya harus menerapkan validasi properti untuk argumen yang diperlukan ini?
Di bawah ini adalah kelas saya:
import pandas as pd
import numpy as np
class HistDollarGains(pd.DataFrame):
@property
def _constructor(self):
return HistDollarGains._internal_ctor
_metadata = ["group", "timestamp_col", "_group", "_timestamp_col"]
@classmethod
def _internal_ctor(cls, *args, **kwargs):
kwargs["group"] = None
kwargs["timestamp_col"] = None
return cls(*args, **kwargs)
def __init__(
self,
data,
group,
timestamp_col,
index=None,
columns=None,
dtype=None,
copy=True,
):
super(HistDollarGains, self).__init__(
data=data, index=index, columns=columns, dtype=dtype, copy=copy
)
self.group = group
self.timestamp_col = timestamp_col
@property
def group(self):
return self._group
@group.setter
def group(self, g):
if g == None:
return
if isinstance(g, str):
group_list = [g]
else:
group_list = g
if not set(group_list).issubset(self.columns):
raise ValueError("Data does not contain " + '[' + ', '.join(group_list) + ']')
self._group = group_list
@property
def timestamp_col(self):
return self._timestamp_col
@timestamp_col.setter
def timestamp_col(self, t):
if t == None:
return
if not t in self.columns:
raise ValueError("Data does not contain " + '[' + t + ']')
self._timestamp_col = t
Berikut ini adalah kasus pengujian saya:
import pytest
import pandas as pd
import numpy as np
from myclass import *
@pytest.fixture(scope="module")
def sample():
samp = pd.DataFrame(
[
{"timestamp": "2020-01-01", "group": "a", "dollar_gains": 100},
{"timestamp": "2020-01-01", "group": "b", "dollar_gains": 100},
{"timestamp": "2020-01-01", "group": "c", "dollar_gains": 110},
{"timestamp": "2020-01-01", "group": "a", "dollar_gains": 110},
{"timestamp": "2020-01-01", "group": "b", "dollar_gains": 90},
{"timestamp": "2020-01-01", "group": "d", "dollar_gains": 100},
]
)
return samp
@pytest.fixture(scope="module")
def sample_obj(sample):
return HistDollarGains(sample, "group", "timestamp")
def test_constructor_without_args(sample):
with pytest.raises(TypeError):
HistDollarGains(sample)
def test_constructor_with_string_group(sample):
hist_dg = HistDollarGains(sample, "group", "timestamp")
assert hist_dg.group == ["group"]
assert hist_dg.timestamp_col == "timestamp"
def test_constructor_with_list_group(sample):
hist_dg = HistDollarGains(sample, ["group", "timestamp"], "timestamp")
def test_constructor_with_invalid_group(sample):
with pytest.raises(ValueError):
HistDollarGains(sample, "invalid_group", np.random.choice(sample.columns))
def test_constructor_with_invalid_timestamp(sample):
with pytest.raises(ValueError):
HistDollarGains(sample, np.random.choice(sample.columns), "invalid_timestamp")
def test_assignment_with_indexed_obj(sample_obj):
b = sample_obj.set_index(sample_obj.group + [sample_obj.timestamp_col])
def test_set_index(sample_obj):
# print(isinstance(a, pd.DataFrame))
assert sample_obj.set_index(sample_obj.group + [sample_obj.timestamp_col]).index.names == ['group', 'timestamp']
sumber
None
nilai tidak valid untukgroup
properti, bukankah seharusnya Anda menaikkanValueError
?None
adalah nilai yang tidak valid, itulah sebabnya saya tidak suka pernyataan if. Tetapi menambahkan bahwa Tidak ada yang membuatnya lulus ujian. Saya mencari cara untuk memperbaikinya dengan benar tanpa pernyataan Tidak ada jika.ValueError
. Masalahnya adalah mencari tahu apa yang mencoba mengaturgroup
atribut untukNone
di tempat pertama.Jawaban:
The
set_index()
Metode akan memanggilself.copy()
secara internal untuk membuat salinan objek DataFrame Anda (lihat kode sumber di sini ), di dalam yang menggunakan metode konstruktor disesuaikan Anda,_internal_ctor()
, untuk membuat objek baru ( sumber ). Catatan yangself._constructor()
identik denganself._internal_ctor()
, yang merupakan metode internal umum untuk hampir semua kelas panda untuk membuat instance baru selama operasi seperti deep-copy atau slicing. Masalah Anda sebenarnya berasal dari fungsi ini:Saya kira Anda menyalin kode ini dari masalah github . Garis-garis
kwargs["**"] = None
secara eksplisit memberitahu konstruktor untuk mengaturNone
keduanyagroup
dantimestamp_col
. Akhirnya setter / validator mendapatkanNone
sebagai nilai baru dan meningkatkan kesalahan.Karena itu, Anda harus menetapkan nilai yang dapat diterima untuk
group
dantimestamp_col
.Kemudian Anda dapat menghapus
if g == None: return
garis di validator.sumber