Python 方法链接而不永久改变对象
目前我正在学习如何编写python类和方法链接。基本上,我想要一个python(2.7)类,它保存我的数据,并具有(可链接的)方法,允许我在不改变原始数据的情况下过滤数据。我用谷歌搜索了一下,似乎我的答案可能与Python 方法链接而不永久改变对象,python,pandas,python-2.x,method-chaining,Python,Pandas,Python 2.x,Method Chaining,目前我正在学习如何编写python类和方法链接。基本上,我想要一个python(2.7)类,它保存我的数据,并具有(可链接的)方法,允许我在不改变原始数据的情况下过滤数据。我用谷歌搜索了一下,似乎我的答案可能与返回self有关,但我不确定如何实现它,以使这些方法不会改变我的原始数据 假设我有一个数据存储在一个名为file的excel文件中,如下所示: +--------+-----+-------+ |个人|性别|得分| +--------+-----+-------+ |A | M | 10|
返回self
有关,但我不确定如何实现它,以使这些方法不会改变我的原始数据
假设我有一个数据存储在一个名为file
的excel文件中,如下所示:
+--------+-----+-------+
|个人|性别|得分|
+--------+-----+-------+
|A | M | 10|
|B | F | 9|
|C | M | 8|
|D | F | 7|
+--------+-----+-------+
我想编写一个名为MyData
的类,这样我就可以进行一些基本的数据调用和过滤
这就是我目前得到的
class MyData:
def __init__ (self, file):
import pandas as pd
self.data = pd.read_excel (file)
self.Person = self.data['Person']
self.Sex = self.data['Sex']
self.Score = self.data['Score']
def male_only(self):
self.data = self.data[self.Sex=="M"]
self.Person = self.Person[self.Sex=="M"]
self.Score = self.Score[self.Sex=="M"]
self.Sex = self.Sex[self.Sex=="M"]
return self
def female_only(self):
self.data = self.data[self.Sex=="F"]
self.Person = self.Person[self.Sex=="F"]
self.Score = self.Score[self.Sex=="F"]
self.Sex = self.Sex[self.Sex=="F"]
return self
这似乎是可行的,但可悲的是,我的原始数据被这段代码永久地改变了。例如:
Data=MyData(文件)
数据
>>>数据
个人性别得分
上午10点零分
1 B F 9
2cm8
三维F 7
Data.male_only().数据
>>>Data.male_only().数据
个人性别得分
上午10点零分
2cm8
数据
>>>数据
个人性别得分
上午10点零分
2cm8
我想要一个类,它为
Data.male_only().Person
和Data.Person.male_only()
或Data.male_only().Data
和Data.Data.male_only()返回相同的答案
无需永久变异Data.Data
或Data.Person
当您编写self.Data=…
时,您可以在第1行中显式修改self.Data。您可以返回数据的新实例:
def male_only(self):
newdata = MyData()
newdata.data = self.data[self.Sex=="M"]
newdata.Person = self.Person[self.Sex=="M"]
newdata.Score = self.Score[self.Sex=="M"]
newdata.Sex = self.Sex[self.Sex=="M"]
return newdata
根据您的意见,这里有一个过滤器解决方案的建议:有一些函数可以激活一些标志/过滤器,然后您必须编写函数来获取属性:
# self.filters should be initialized to [] in __init__
def male_only(self):
self.filters.append('male_only')
def person(self):
if "male_only" in self.filters:
return self.Person[self.Sex=="M"]
else:
return self.Person
要想知道这是否有意义,你应该真正完成你的测试用例来帮助你修正你的想法(最好先写测试用例,然后再写类)。我想详细说明一下@Demi Lune的答案。我认为没有一种方法可以绕过创建
MyData
实例、修改它并从链方法返回它。这类事情首先起作用的全部原因是,所有的链方法都属于同一个类,并且它们返回该类的一个实例
例如,str.swapcase
、str.zfill
和str.replace
都是str
的一部分,它们都返回str
s
>>> string = "Hello World"
>>> string.swapcase().zfill(16).replace("L", "T")
'00000hETTO wORTD'
>>> string
'Hello World'
>>>
您试图做的(
Data.Person.male_only()
)打破了这种模式,因为现在暗示方法male_only
不是MyData
类的一部分,而是属于Person
对象的方法。什么是self.Person
或self.data[“Person”]
?我对熊猫不是很熟悉。它是一根绳子吗?字符串列表?在任何情况下,不管是什么,您试图实现的基本目标都是在该类型的类中添加一个名为male\u only
的新方法。我同意@Demi Lune
我更改了OP的代码,以便male_only()
和memale_only()
方法始终返回其所属对象的副本。我更改了\uuuu init\uuuu()
方法,因为我认为您不希望每次创建新对象时都调用pd.read\u csv()
方法。所以male\u only()
和memale\u only()
方法总是返回新对象,它不会对其他对象产生影响
import pandas as pd
# Added for creating file on memory.
import io
csv = '''Person,Sex,Score
p1,M,1
p2,M,2
p3,M,3
p4,F,4
p5,F,5
p6,F,6'''
file = io.StringIO(csv)
class MyData:
def __init__ (self, file=None, data=None):
import pandas as pd
if file:
self.data = pd.read_csv(file)
else:
self.data = data
self.Person = self.data['Person']
self.Sex = self.data['Sex']
self.Score = self.data['Score']
def copy_d(self):
return MyData(data=self.data.copy())
def male_only(self):
d = self.copy_d()
d.data = self.data[self.Sex=="M"]
d.Person = self.Person[self.Sex=="M"]
d.Score = self.Score[self.Sex=="M"]
d.Sex = self.Sex[self.Sex=="M"]
return d
def female_only(self):
d = self.copy_d()
d.data = self.data[self.Sex=="F"]
d.Person = self.Person[self.Sex=="F"]
d.Score = self.Score[self.Sex=="F"]
d.Sex = self.Sex[self.Sex=="F"]
return d
d = MyData(file)
print(d.female_only().data)
# Person Sex Score
# 3 p4 F 4
# 4 p5 F 5
# 5 p6 F 6
print(d.male_only().data)
# Person Sex Score
# 0 p1 M 1
# 1 p2 M 2
# 2 p3 M 3
print(d.data)
# Person Sex Score
# 0 p1 M 1
# 1 p2 M 2
# 2 p3 M 3
# 3 p4 F 4
# 4 p5 F 5
# 5 p6 F 6
但是,如果您只是使用pandas.DataFrame
,另一种方法就是使用裸pandas.DataFrame
。首先,在大多数情况下,pandas.DataFrame
对象已经具有与列名称相等的属性名称。所以实际上,您不需要定义诸如Person
、Sex
、Score
之类的属性,因为它已经存在于DataFrame对象中
即:
因此,您的仅限男性()
和仅限女性()
方法编写如下
import pandas as pd
# Added for creating file on memory.
import io
csv = '''Person,Sex,Score
p1,M,1
p2,M,2
p3,M,3
p4,F,4
p5,F,5
p6,F,6'''
file = io.StringIO(csv)
def male_only(df):
return df[df.Sex=='M']
def female_only(df):
return df[df.Sex=='F']
df = pd.read_csv(file)
male_only(df)
# In [1034]: male_only(df)
# Out[1037]:
# Person Sex Score
# 0 p1 M 1
# 1 p2 M 2
# 2 p3 M 3
female_only(df)
# In [1038]: female_only(df)
# Out[1041]:
# Person Sex Score
# 3 p4 F 4
# 4 p5 F 5
# 5 p6 F 6
我希望它能对您有所帮助。如果您不想改变正在调用这些方法的对象,那么显然您需要返回一个新对象,而不是
self
。您当前的\uuuu init\uuuu()
并不是很理想,因为它只允许您从文件而不是从现有的数据帧创建新的MyData
。@jasonharper我对这一点非常陌生,但我目前的理解是返回自我
对于方法链接是必不可少的。。。对于Data.male_only().Person
和Data.Person.male_only()
返回相同答案而不永久更改Data.Person
的代码示例,将不胜感激。非常感谢。谢谢你的回答。然而,我不认为这是我要找的。例如,此解决方案适用于Data.Person.male_only().Person
,但对于Data.Person.male_only()
,它将失败。然后,另一种可能是使用某种“查看/筛选”功能管理对象。你应该用更多你想做的例子或测试用例来丰富你的问题。我会用“过滤”解决方案来编辑我的答案。这不是我想要的,但肯定是一个很酷的技巧。person
方法实际上非常有用。非常感谢更新的答案。非常感谢。你的回答很有帮助,我想这就是我想要的。只是澄清一下,它并不是专门使用pandas.DataFrame
。这只是一个关于python类的练习,该类包含允许用户操作返回对象而无需永久更改的方法
import pandas as pd
# Added for creating file on memory.
import io
csv = '''Person,Sex,Score
p1,M,1
p2,M,2
p3,M,3
p4,F,4
p5,F,5
p6,F,6'''
file = io.StringIO(csv)
def male_only(df):
return df[df.Sex=='M']
def female_only(df):
return df[df.Sex=='F']
df = pd.read_csv(file)
male_only(df)
# In [1034]: male_only(df)
# Out[1037]:
# Person Sex Score
# 0 p1 M 1
# 1 p2 M 2
# 2 p3 M 3
female_only(df)
# In [1038]: female_only(df)
# Out[1041]:
# Person Sex Score
# 3 p4 F 4
# 4 p5 F 5
# 5 p6 F 6