Python 在_init中为用户类设置默认/空属性__
我有一个相当不错的编程水平,并且从这里的社区获得了很多价值。然而,我从来没有在编程方面进行过太多的学术教学,也没有和真正有经验的程序员一起工作过。因此,我有时会与“最佳实践”斗争 我找不到一个更好的地方来回答这个问题,尽管很可能有人讨厌这类问题,我还是发布了这篇文章。如果这让你不高兴,我很抱歉。我只是想学习,不是惹你生气 问题: 当我创建一个新类时,我是否应该在init中设置所有实例属性,即使它们都不是,而且事实上后来在类方法中指定了值 有关MyClass的属性结果,请参见下面的示例:Python 在_init中为用户类设置默认/空属性__,python,class,instance,instance-variables,python-attrs,Python,Class,Instance,Instance Variables,Python Attrs,我有一个相当不错的编程水平,并且从这里的社区获得了很多价值。然而,我从来没有在编程方面进行过太多的学术教学,也没有和真正有经验的程序员一起工作过。因此,我有时会与“最佳实践”斗争 我找不到一个更好的地方来回答这个问题,尽管很可能有人讨厌这类问题,我还是发布了这篇文章。如果这让你不高兴,我很抱歉。我只是想学习,不是惹你生气 问题: 当我创建一个新类时,我是否应该在init中设置所有实例属性,即使它们都不是,而且事实上后来在类方法中指定了值 有关MyClass的属性结果,请参见下面的示例: clas
class MyClass:
def __init__(self,df):
self.df = df
self.results = None
def results(df_results):
#Imagine some calculations here or something
self.results = df_results
我在其他项目中发现,当类属性只出现在类方法中时,它们可能会被掩埋,而且会有很多变化
那么对于一个经验丰富的专业程序员来说,这方面的标准做法是什么呢?为了可读性,您会在init中定义所有实例属性吗
如果任何人有任何链接的材料,我可以找到这样的原则,然后请把他们在一个答案,这将是非常感谢。我知道PEP-8,并且已经搜索了我上面的问题好几次,没有找到任何人涉及这个问题
谢谢
<>安迪< /P> < P >为了理解在<代码>中的初始化属性的重要性(或不),我们以你的类的修改版本<代码> MyClass < /代码>为例。本课程的目的是根据学生姓名和分数计算科目的分数。您可以在Python解释器中进行后续操作
>>> class MyClass:
... def __init__(self,name,score):
... self.name = name
... self.score = score
... self.grade = None
...
... def results(self, subject=None):
... if self.score >= 70:
... self.grade = 'A'
... elif 50 <= self.score < 70:
... self.grade = 'B'
... else:
... self.grade = 'C'
... return self.grade
在这一点上,我们了解到,我们必须提供学生的姓名
和科目的分数
作为最低要求,但分数
现在并不重要,因为这将在以后的结果
方法中进行计算。因此,我们只使用self.grade=None
,而不将其定义为位置参数。让我们初始化一个类实例(对象):
这清楚地给出了所有初始属性及其值的dict
视图。请注意,grade
有一个None
值,如\uuuu init\uuuu
中指定的
让我们花点时间来了解\uuuuu init\uuuu
的作用。有许多在线资源可用于解释此方法的作用,但我将总结:
与\uuuu init\uuuu
类似,Python有另一个内置方法,名为\uuuu new\uuuu()
。当您像这样创建一个class对象x=MyClass(name='John',score=70)
时,Python首先在内部调用\uuuu new\uuu()
来创建类MyClass
的一个新实例,然后调用\uu init\uuuuu
来初始化属性name
和score
。当然,在这些内部调用中,当Python找不到所需位置参数的值时,就会产生一个错误,正如我们上面看到的。换句话说,初始化属性。您可以为name
和score
分配新的初始值,如下所示:
>>> x.__init__(name='Tim', score=50)
>>> x.__dict__
{'name': 'Tim', 'score': 50, 'grade': None}
>>> x = MyClass(name=['John', 'Tom', 'Sean'], score=[70, 55, 40])
>>> x.name
['John', 'Tom', 'Sean']
>>> x.score
[70, 55, 40]
也可以访问如下所示的单个属性<代码>等级
不提供任何信息,因为它是无
>>> x.name
'Tim'
>>> x.score
50
>>> x.grade
>>>
在结果
方法中,您会注意到主题
“变量”被定义为无
,一个位置参数。此变量的作用域仅在此方法内。为了演示的目的,我在这个方法中明确定义了subject
,但它也可以在中初始化。但如果我尝试使用我的对象访问它会怎么样:
>>> x.subject
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'subject'
这将打印分数的等级,并注意当我们查看属性时,等级
也已更新。从一开始,我们就清楚地了解了初始属性及其值是如何变化的
但是主题呢?如果我想知道蒂姆的数学成绩和分数,我可以很容易地访问分数
和分数
,就像我们以前看到的那样,但是我怎么知道这个科目呢?由于subject
变量是results
方法范围的局部变量,因此我们可以返回subject
的值。更改results
方法中的return
语句:
def results(self, subject=None):
#<---code--->
return self.grade, subject
要访问元组中的值,让我们将它们分配给变量。在Python中,只要变量的数量等于集合的长度,就可以将集合中的值分配给同一表达式中的多个变量。在这里,长度只有两个,因此我们可以在表达式的左边有两个变量:
>>> grade, subject = x.results(subject='Math')
>>> subject
'Math'
因此,我们有了它,尽管它需要一些额外的代码行来获得主题
。使用点运算符访问带有x.的属性,一次访问所有属性会更直观。。如果你想读一些优秀的答案:
基于对默认参数的简要讨论,我们的类声明将修改为:
class MyClass:
def __init__(self,names=None, scores=None):
self.names = names if names else []
self.scores = scores if scores else []
self.grades = []
#<---code------>
grades
是一个空列表,表明调用results()
时将为多个学生计算成绩。因此,我们的结果
方法也应该修改。我们现在进行的比较应该是分数数字(70、50等)和self.scores
列表中的项目之间的比较,同时self.grades
列表也应该用各个分数更新。将结果方法更改为:
def results(self, subject=None):
#Grade calculator
for i in self.scores:
if i >= 70:
self.grades.append('A')
elif 50 <= i < 70:
self.grades.append('B')
else:
self.grades.append('C')
return self.grades, subject
这看起来不错,但想象一下,如果名单很大,要弄清楚谁的分数/等级属于谁,那绝对是一场噩梦。在这一点上,重要的是使用正确的数据类型初始化属性,该数据类型可以轻松地存储所有这些项
>>> x.results(subject='Math')
('B', 'Math')
>>> grade, subject = x.results(subject='Math')
>>> subject
'Math'
>>> x = MyClass(name=['John', 'Tom', 'Sean'], score=[70, 55, 40])
>>> x.name
['John', 'Tom', 'Sean']
>>> x.score
[70, 55, 40]
#Not recommended
class MyClass:
def __init__(self,names=[]):
self.names = names
self.names.append('Random_name')
>>> x = MyClass()
>>> x.names
['Random_name']
>>> y = MyClass()
>>> y.names
['Random_name', 'Random_name']
>>> id(x.names)
2513077313800
>>> id(y.names)
2513077313800
#Recommended
>>> class MyClass:
... def __init__(self,names=None):
... self.names = names if names else []
... self.names.append('Random_name')
>>> x = MyClass()
>>> x.names
['Random_name']
>>> y = MyClass()
>>> y.names
['Random_name']
>>> y = MyClass(names=['Viky','Sam'])
>>> y.names
['Viky', 'Sam', 'Random_name']
>>> x.names
['Random_name']
class MyClass:
def __init__(self,names=None, scores=None):
self.names = names if names else []
self.scores = scores if scores else []
self.grades = []
#<---code------>
>>> x.names
['John', 'Tom', 'Sean']
>>> x.grades
[]
def results(self, subject=None):
#Grade calculator
for i in self.scores:
if i >= 70:
self.grades.append('A')
elif 50 <= i < 70:
self.grades.append('B')
else:
self.grades.append('C')
return self.grades, subject
>>> x.results(subject='Math')
>>> x.grades
['A', 'B', 'C']
>>> x.names
['John', 'Tom', 'Sean']
>>> x.scores
[70, 55, 40]
class MyClass:
"""A class that computes the final results for students"""
def __init__(self,names_scores=None):
"""initialize student names and scores
:param names_scores: accepts key/value pairs of names/scores
E.g.: {'John': 70}"""
self.names_scores = names_scores if names_scores else {}
def results(self, _final_results={}, subject=None):
"""Assign grades and collect final results into a dictionary.
:param _final_results: an internal arg that will store the final results as dict.
This is just to give a meaningful variable name for the final results."""
self._final_results = _final_results
for key,value in self.names_scores.items():
if value >= 70:
self.names_scores[key] = [value,subject,'A']
elif 50 <= value < 70:
self.names_scores[key] = [value,subject,'B']
else:
self.names_scores[key] = [value,subject,'C']
self._final_results = self.names_scores #assign the values from the updated names_scores dict to _final_results
return self._final_results
>>> x = MyClass(names_scores={'John':70, 'Tom':50, 'Sean':40})
>>> x.results(subject='Math')
{'John': [70, 'Math', 'A'],
'Tom': [50, 'Math', 'B'],
'Sean': [40, 'Math', 'C']}
>>> y = x.results(subject='Math')
>>> y['John']
[70, 'Math', 'A']
x = MyClass('John', 70) #not explicit
x = MyClass(name='John', score=70) #explicit
class MyClass1:
def __init__(self, df):
self.df = df
self.results = None
def set_results(self, df_results):
self.results = df_results
def get_results(self):
return self.results
class MyClass2:
def __init__(self, df):
self.df = df
def set_results(self, df_results):
self.results = df_results
def get_results(self):
return self.results
MyClass1("df").get_results()
# returns None
MyClass2("df").get_results()
# Traceback (most recent call last):
# ...
# AttributeError: 'MyClass2' object has no attribute 'results'
class MyClassBuilder:
def __init__(self, df):
self._df = df # df is known immediately
# give a default value to other fields if possible
def results(self, df_results):
self._results = df_results
return self # for fluent style
... other field initializers
def build(self):
return MyClass(self._df, self._results, ...)
class MyClass:
def __init__(self, df, results, ...):
self.df = df
self.results = results
...
def get_results(self):
return self.results
... other getters
>>> b = MyClassBuilder("df").build()
Traceback (most recent call last):
...
AttributeError: 'MyClassBuilder' object has no attribute '_results'
>>> b = MyClassBuilder("df")
>>> b.results("r")
... other fields iniialization
>>> x = b.build()
>>> x
<__main__.MyClass object at ...>
>>> x.get_results()
'r'
class MyClass:
def __init__(self,df):
self.df = df
self._results = None
@property
def results(self):
if self._results is None:
raise Exception('df_client is None')
return self._results
def generate_results(self, df_results):
#Imagine some calculations here or something
self._results = df_results