Python 在_init中为用户类设置默认/空属性__

Python 在_init中为用户类设置默认/空属性__,python,class,instance,instance-variables,python-attrs,Python,Class,Instance,Instance Variables,Python Attrs,我有一个相当不错的编程水平,并且从这里的社区获得了很多价值。然而,我从来没有在编程方面进行过太多的学术教学,也没有和真正有经验的程序员一起工作过。因此,我有时会与“最佳实践”斗争 我找不到一个更好的地方来回答这个问题,尽管很可能有人讨厌这类问题,我还是发布了这篇文章。如果这让你不高兴,我很抱歉。我只是想学习,不是惹你生气 问题: 当我创建一个新类时,我是否应该在init中设置所有实例属性,即使它们都不是,而且事实上后来在类方法中指定了值 有关MyClass的属性结果,请参见下面的示例: clas

我有一个相当不错的编程水平,并且从这里的社区获得了很多价值。然而,我从来没有在编程方面进行过太多的学术教学,也没有和真正有经验的程序员一起工作过。因此,我有时会与“最佳实践”斗争

我找不到一个更好的地方来回答这个问题,尽管很可能有人讨厌这类问题,我还是发布了这篇文章。如果这让你不高兴,我很抱歉。我只是想学习,不是惹你生气

问题:

当我创建一个新类时,我是否应该在init中设置所有实例属性,即使它们都不是,而且事实上后来在类方法中指定了值

有关MyClass的属性结果,请参见下面的示例:

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