Closed tower-town closed 1 year ago
当时好像设计他们的时候好像就是作为参数来的,而不是作为一个对象,我的理解是这样的,这个不是我设计的😂 @GalaxySnail 您来解释解释?
当时好像设计他们的时候好像就是作为参数来的,而不是作为一个对象,我的理解是这样的,这个不是我设计的😂
@GalaxySnail 您来解释解释?
但你是项目作者,可以进行全局改动🤔
emmmm 我有点没太理解,为什么这里会有强耦合(捂脸)能否详细解释一下?按我的理解,dataclass 这种设计就是降低耦合的
但你是项目作者,可以进行全局改动🤔
但是我觉得那个样子好像挺好的。。。。。本来好像就是作为参数,然后dataclass
注解也能减少不必要的方法啥的(方便偷懒
emmmm 我有点没太理解,为什么这里会有强耦合(捂脸)能否详细解释一下?按我的理解,dataclass 这种设计就是降低耦合的
有许多函数的参数输入和输出都必须符合Hack NewsItem的属性,可以将公共属性交给class的方法处理,然后整合属性交给一个特定的函数.
但你是项目作者,可以进行全局改动🤔
但是我觉得那个样子好像挺好的。。。。。本来好像就是作为参数,然后
dataclass
注解也能减少不必要的方法啥的(方便偷懒
你是项目作者,这个仓库你说的算
你的意思是提供一个替代默认 __init__
的构造函数吗?请问能否写一段示例代码,我不确定我是否理解了你的意思(
你是项目作者,这个仓库你说的算
不能这么说,这里面有人家的代码了,我也要对他的代码负责,那是他的劳动成果
请问能否写一段示例代码
啊我不是这个意思,我是没理解提问者想要把代码改成什么样,所以想让他提供一段示例代码看看他是怎么想的
啊我不是这个意思,我是没理解提问者想要把代码改成什么样,所以想让他提供一段示例代码看看他是怎么想的
哦哦,好的
你的意思是提供一个替代默认
__init__
的构造函数吗?请问能否写一段示例代码,我不确定我是否理解了你的意思( 提供一个替代默认__init__
的构造函数吗 类似的 下面是示例代码class NameID() def __init__(self): self.name: str = "" self.id: str = "" def getNameId(self,name, id): self.name = name, self.id = id def print(self): printf(f"name is {self.name}, id is {self.id}") ....
面向对象编程了是嘛😂万恶的java
而不是这样
@dataclass
class NameID:
name: str
id: str
....
def getNameID(name, id) -> NameID:
NameID["name"] = name
NameID["id"] = id
def print(data:NameID):
printf(f"name is {data["name"]}, id is {data["id"]}")
class NameID() def __init__(self): self.name: str = "" self.id: str = "" def getNameId(self,name, id): self.name = name, self.id = id def print(self): printf(f"name is {self.name}, id is {self.id}") ....
嗯……这样的做法有一个问题,那就是实例化后的新对象处于一个“未完全初始化”的状态,每个字段都赋予了一个无意义的空值,那在其它地方处理它的时候就不得不处理字段为空值的情况。而对于 dataclass 来说,一旦对象构造完成,这个对象就是完整的了,可以假设它的每个字段都有一个合适的有意义的值,可以随意操作。
而不是这样
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: NameID["name"] = name NameID["id"] = id def print(data:NameID): printf(f"name is {data["name"]}, id is {data["id"]}")
这里的代码不太对,也许你可以先阅读一下标准库 dataclasses 的文档,链接在上面有。也可以看看这篇文章,里面解释了 dataclass 的核心思想。其中一个很重要的思想就是,在 init 函数里不要做额外的工作,只是对属性赋值,并且保证对象一旦构造完成就一定处于完全初始化的状态,不会有“初始化了一半”的情况。
类型提示虽然看上去是写在类属性的位置,但描述的不是类属性,而是实例属性,也就是相当于:
class NameID:
def __init__(self, name: str, id: str):
self.name = name
self.id = id
... # 除了 init 之外还自动生成了一些别的方法
要构造一个新的 NameID
对象不需要额外的方法,直接调用 name_id = NameID(name, id)
就可以了。同时也不需要专门的 print 方法,dataclass 自动生成了 __repr__
特殊方法,所以可以直接调用内置函数 print 来做:
>>> print(name_id)
NameID(name='foo', id='bar')
class NameID() def __init__(self): self.name: str = "" self.id: str = "" def getNameId(self,name, id): self.name = name, self.id = id def print(self): printf(f"name is {self.name}, id is {self.id}") ....
嗯……这样的做法有一个问题,那就是实例化后的新对象处于一个“未完全初始化”的状态,每个字段都赋予了一个无意义的空值,那在其它地方处理它的时候就不得不处理字段为空值的情况。而对于 dataclass 来说,一旦对象构造完成,这个对象就是完整的了,可以假设它的每个字段都有一个合适的有意义的值,可以随意操作。
class NameID 初始化工作交给main函数调用完成
而不是这样
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: NameID["name"] = name NameID["id"] = id def print(data:NameID): printf(f"name is {data["name"]}, id is {data["id"]}")
这里的代码不太对,也许你可以先阅读一下标准库 dataclasses 的文档,链接在上面有。也可以看看这篇文章,里面解释了 dataclass 的核心思想。其中一个很重要的思想就是,在 init 函数里不要做额外的工作,只是对属性赋值,并且保证对象一旦构造完成就一定处于完全初始化的状态,不会有“初始化了一半”的情况。
类型提示虽然看上去是写在类属性的位置,但描述的不是类属性,而是实例属性,也就是相当于:
这个不重要因为我在
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: NameID["name"] = name NameID["id"] = id def print(data:NameID): printf(f"name is {data["name"]}, id is {data["id"]}")
加入了 ... 代表省略不重要步骤
要构造一个新的 NameID 对象不需要额外的方法,直接调用 name_id = NameID(name, id) 就可以了。同时也不需要专门的 print 方法,dataclass 自动生成了 repr 特殊方法,所以可以直接调用内置函数 print 来做:
print(name_id) NameID(name='foo', id='bar')
这只是一个例子的print
方法作为示例调用函数,所以不用关心具体的的作用
虽然dataclasses
有这样的好处,在本仓库中的大多数调用函数必须完成返回和输入参数与HackerNewsItem的数据处理。但有些函数明明只应该完成提取数据,但其既要处提取数据也要提供数据处理,犯了函数设计的忌讳。
这个不重要因为我在
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: NameID["name"] = name NameID["id"] = id def print(data:NameID): printf(f"name is {data["name"]}, id is {data["id"]}")
加入了 ... 代表省略不重要步骤
不是省略不重要步骤的问题,而是这里的 getNameID 函数写的是错的,没法正常运行,你可以自己尝试一下。(顺便一提,python 编码规范 PEP 8 推荐使用 snake_case 而不是 camelCase)
虽然
dataclasses
有这样的好处,在本仓库中的大多数调用函数必须完成返回和输入参数与HackerNewsItem的数据处理。但有些函数明明只应该完成提取数据,但其既要处提取数据也要提供数据处理,犯了函数设计的忌讳。
这个我觉得是误解。dataclass 是一种面向对象的思想,是以数据为中心,一个 dataclass 对象就是一个数据,你可以在一些函数里生产数据,然后在别的函数消费数据。换言之,是让数据自己作为接口,dataclass 自身即接口,这是一种耦合很松的做法,而且接口定义清晰明确。也正因此,dataclass 的 __init__
函数是不包括数据处理的逻辑的,它只是简单提取数据而已。数据处理的逻辑在消费 dataclass 的函数里完成。
class NameID 初始化工作交给main函数调用完成
如上所述,dataclass 以数据为中心,数据初始化的工作应该由数据自己完成,而获取数据的函数只需要给 dataclass 提供一些构造 dataclass 所必需的数据,dataclass 就可以自己完成构造,而不应该获取数据的函数来操心如何一步步初始化这个对象。实际上,main 函数需要做的本身也很简单,只要 main 函数有 name
和 id
的值,那就是一步 name_id = NameID(name, id)
的事罢了。
dataclass 是一种面向对象的思想,是以数据为中心,一个 dataclass 对象就是一个数据,你可以在一些函数里生产数据,然后在别的函数消费数据。换言之,是让数据自己作为接口,dataclass 自身即接口,这是一种耦合很松的做法,而且接口定义清晰明确。也正因此,dataclass 的
__init__
函数是不包括数据处理的逻辑的,它只是简单提取数据而已。数据处理的逻辑在消费 dataclass 的函数里完成。
学到了 佬!Orz
这个不重要因为我在
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: NameID["name"] = name NameID["id"] = id def print(data:NameID): printf(f"name is {data["name"]}, id is {data["id"]}")
加入了 ... 代表省略不重要步骤
不是省略不重要步骤的问题,而是这里的 getNameID 函数写的是错的,没法正常运行,你可以自己尝试一下。(顺便一提,python 编码规范 PEP 8 推荐使用 snake_case 而不是 camelCase)
虽然
dataclasses
有这样的好处,在本仓库中的大多数调用函数必须完成返回和输入参数与HackerNewsItem的数据处理。但有些函数明明只应该完成提取数据,但其既要处提取数据也要提供数据处理,犯了函数设计的忌讳。这个我觉得是误解。dataclass 是一种面向对象的思想,是以数据为中心,一个 dataclass 对象就是一个数据,你可以在一些函数里生产数据,然后在别的函数消费数据。换言之,是让数据自己作为接口,dataclass 自身即接口,这是一种耦合很松的做法,而且接口定义清晰明确。也正因此,dataclass 的
__init__
函数是不包括数据处理的逻辑的,它只是简单提取数据而已。数据处理的逻辑在消费 dataclass 的函数里完成。class NameID 初始化工作交给main函数调用完成
如上所述,dataclass 以数据为中心,数据初始化的工作应该由数据自己完成,而获取数据的函数只需要给 dataclass 提供一些构造 dataclass 所必需的数据,dataclass 就可以自己完成构造,而不应该获取数据的函数来操心如何一步步初始化这个对象。实际上,main 函数需要做的本身也很简单,只要 main 函数有
name
和id
的值,那就是一步name_id = NameID(name, id)
的事罢了。
你的回答并不能没有解决函数的设计的问题,明明数据在公共属性里,一个函数的功能只完成一件事。而在本仓库中的函数大量与公共属性交互。而此例中有大量的函数需要返回指定类型的数据,但明明不是该函数应该做的事情。一些函数值需要完成数据的挂载。
这个不重要因为我在
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: NameID["name"] = name NameID["id"] = id def print(data:NameID): printf(f"name is {data["name"]}, id is {data["id"]}")
加入了 ... 代表省略不重要步骤
不是省略不重要步骤的问题,而是这里的 getNameID 函数写的是错的,没法正常运行,你可以自己尝试一下。(顺便一提,python 编码规范 PEP 8 推荐使用 snake_case 而不是 camelCase)
虽然
dataclasses
有这样的好处,在本仓库中的大多数调用函数必须完成返回和输入参数与HackerNewsItem的数据处理。但有些函数明明只应该完成提取数据,但其既要处提取数据也要提供数据处理,犯了函数设计的忌讳。这个我觉得是误解。dataclass 是一种面向对象的思想,是以数据为中心,一个 dataclass 对象就是一个数据,你可以在一些函数里生产数据,然后在别的函数消费数据。换言之,是让数据自己作为接口,dataclass 自身即接口,这是一种耦合很松的做法,而且接口定义清晰明确。也正因此,dataclass 的
__init__
函数是不包括数据处理的逻辑的,它只是简单提取数据而已。数据处理的逻辑在消费 dataclass 的函数里完成。class NameID 初始化工作交给main函数调用完成
如上所述,dataclass 以数据为中心,数据初始化的工作应该由数据自己完成,而获取数据的函数只需要给 dataclass 提供一些构造 dataclass 所必需的数据,dataclass 就可以自己完成构造,而不应该获取数据的函数来操心如何一步步初始化这个对象。实际上,main 函数需要做的本身也很简单,只要 main 函数有
name
和id
的值,那就是一步name_id = NameID(name, id)
的事罢了。
再次强调不要谈与本仓库无关的概念而非本仓库的具体问题
不要关注于初始化工作,如果想让初始化简单,完全可以将初始化函数放到 __init__
中
再次强调不要谈与本仓库无关的概念而非本仓库的具体问题
有没有可能在第二部分他解释的就是你所提出的问题
明明数据在公共属性里,一个函数的功能只完成一件事。而在本仓库中的函数大量与公共属性交互
为什么函数不能和公共属性交互?难道函数处理的数据只能通过以参数的形式传递进来吗?
整个项目都是单线程的,没有多线程操作,操作公共数据是不会对数据有误操作的
有大量的函数需要返回指定类型的数据,但明明不是该函数应该做的事情。一些函数值需要完成数据的挂载。
每个函数完成他需要做的事,然后返回相关的数据,我认为是没有问题的
可能有些函数体确实过大,内部可以抽离出来一些其他的函数进行处理,但是抽离出来了也仅仅只是在那个函数里调用一次,我认为是没有必要再一次抽出来,再做一层函数的。由于函数体过大而导致函数过程没有那么容易理解,但这个问题或许可能通过注释解决
可能会出现的问题
@dataclass
class NameID:
name: str
id: str
....
def getNameID(name, id) -> NameID:
...
当更改NameID
的数据
@dataclass
class NameID:
name: str
id: str,
email: str
....
def getNameID(name, id) -> NameID:
...
# 则需要更改`getNameID`,因为可能`getNameID`并没有处理`email`参数即将`email`参数调回默认值
def getEmail(NameID, email) -> NameID:
...
而只要
class NameID()
def __init__(self):
self.name: str = ""
self.id: str = ""
def getNameId(self,name, id):
self.name = name,
self.id = id
def getEmail(self,mail):
self.email = email
这个问题就是我的问题核心
如果每一个调用NameID
不需要处理额外添加数据的返回值,则就没有问题
对了
class NameID()
def __init__(self):
self.name: str = ""
self.id: str = ""
self.email = ""
如果每一个调用
NameID
不需要处理额外添加数据的返回值,则就没有问题 这个问题就是我的问题核心
我们确实不需要再添加值了,因为我们所需要的信息就只用这么多
用了dataclass注解之后类我觉得那个类更像是java里的Entity,他们基本上是不会有太大的变化了,而且那两个类确实不会再变化了(确定
class NameID() def __init__(self): self.name: str = "" self.id: str = "" self.email = ""
class NameID:
def __init__(self):
self.name: str = ""
self.id: str = ""
self.email: str = ""
没有问题就ok
再次强调不要谈与本仓库无关的概念而非本仓库的具体问题
回到本仓库的具体问题,我认为目前的代码完美实现了本仓库的需求,没有更改的必要,over。
可能会出现的问题
@dataclass class NameID: name: str id: str .... def getNameID(name, id) -> NameID: ...
当更改
NameID
的数据@dataclass class NameID: name: str id: str, email: str .... def getNameID(name, id) -> NameID: ... # 则需要更改`getNameID`,因为可能`getNameID`并没有处理`email`参数即将`email`参数调回默认值 def getEmail(NameID, email) -> NameID: ...
而只要
class NameID() def __init__(self): self.name: str = "" self.id: str = "" def getNameId(self,name, id): self.name = name, self.id = id def getEmail(self,mail): self.email = email
那么说明这里的 name, id, email 属性不是绑定在一起的,解决这个问题有两种办法,一种是继承,一种是组合。
继承:
from typing import Self
@dataclass
class NameID:
name: str
id: str
@dataclass
class NameIDEmail(NameID):
email: str
@classmethod
def from_nameid_and_email(cls, name_id: NameID, email: str) -> Self:
return cls(name_id.name, name_id.id, email)
组合:
@dataclass
class NameID:
name: str
id: str
@dataclass
class NameIDEmail:
name_id: NameID
email: str
(这种情况下,通常组合比继承更好。不过也要看情况。)
或者还有一种办法,如果对于具体需求来说 email 确实是个可选的属性,那可以让它可选:
@dataclass
class NameID:
name: str
id: str
email: str | None = None
这样能让用户之后随时自己设置 email 属性。
在
main.py
中以及
与函数参数形成强耦合,进而每当修改Hacker NewsItem属性将影响全局函数的类型检查。建议将公共属性统一放到一个class的属性里,比如