V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
cz5424
V2EX  ›  问与答

求 Python 大佬看看哪个版本的代码比较好,类化争议

  •  
  •   cz5424 · 2018-02-11 10:53:12 +08:00 · 2236 次点击
    这是一个创建于 2237 天前的主题,其中的信息可能已经有所发展或是发生改变。

    近期一个项目要写一个类,跟同学争执不下,特来请教各位大佬

    import time
    
    class SchoolDate(object):
    
        def __init__(self, school_time):
            self.weeks = 1
            self.what_day = 1
            self.school_time = time.strptime(school_time, "%Y-%m-%d")
    
        def get_term(self):
            timestamp = time.mktime(self.school_time)
            #计算开学前周日的时间戳
            first_week_timestamp=timestamp- 86400* int(time.strftime("%w",self.school_time))
            now_timestamp = time.time()
            self.what_day = time.strftime("%w",time.localtime(now_timestamp))
            #周数 = (当前时间戳 - 开学周日的时间戳) / 7 天 +1
            self.weeks = int((time.time() -first_week_timestamp)/ (86400*7)) + 1
            if self.weeks < 1:
                self.what_day = 1
                self.weeks = 1
            return self
    
        def weeks(self):
            return self.weeks
    
        def day(self):
            return self.what_day
    
    
    

    版本 2

    
    import time
    from datetime import datetime
    
    
    class SchoolDate(object):
        __get = False
    
        def __init__(self, starting_date):
            """
            :param starting_date: 开学日期
            """
            self.now_weeks = 1
            self.what_day = 1
            self.starting_date = time.strptime(starting_date, "%Y-%m-%d")
    
        def _get_term(self):
            self.__get = True
            timestamp = time.mktime(self.starting_date)
            # 计算开学前周日的时间戳
            first_week_timestamp = timestamp - 86400 * int(time.strftime("%w", self.starting_date))
            now_timestamp = time.time()
            self.what_day = time.strftime("%w", time.localtime(now_timestamp))
            # 周数 = (当前时间戳 - 开学周日的时间戳) / 7 天 +1
            self.now_weeks = int((time.time() - first_week_timestamp) / (86400*7)) + 1
            if self.weeks < 1:
                self.what_day = 1
                self.now_weeks = 1
    
        @property
        def weeks(self):
            """开学第几周"""
            if not self.__get:
                self._get_term()
            return self.now_weeks
    
        @property
        def week_day(self):
            """周几"""
            if not self.__get:
                self._get_term()
            return self.what_day
    
        @property
        def school_year(self):
            """获取当前学年"""
            now = datetime.now()
            if 2 <= now.month <= 7:
                return "%d-%d" % ((now.year - 1), now.year)
            else:
                return "%d-%d" % (now.year, (now.year + 1))
    
    

    初级写法,敬请吐槽!!

    17 条回复    2018-02-11 15:13:28 +08:00
    msg7086
        1
    msg7086  
       2018-02-11 10:59:33 +08:00
    school_year 这个方法只有版本 2 里有。
    __get 这个只是数据缓存标志位吧。
    最理想的做法大概是用非侵入式元编程来智能缓存,但是说白了,没有数据量要求,怎么写都行。更重要的是可读性可维护性,缓存什么的无所谓啦。就说版本 1,如果有一天客户说,我们流量大了要提速,让你改成版本 2,你觉得会花多久?
    cz5424
        2
    cz5424  
    OP
       2018-02-11 11:06:39 +08:00 via Android
    这个类不只是做周数计算,而且我想吐槽版本 1 的一点是,必须调用 get_term 才能获取下面两个参数
    @msg7086
    msg7086
        4
    msg7086  
       2018-02-11 11:08:52 +08:00
    sd.get_term().weeks()?链式调用我觉得也还行。

    这个类到底是做什么的你也没说啊。
    cz5424
        5
    cz5424  
    OP
       2018-02-11 11:10:12 +08:00 via Android
    调用版本一
    school = shooldate().get_term()
    school.weeks()
    cz5424
        6
    cz5424  
    OP
       2018-02-11 11:11:09 +08:00 via Android
    @msg7086 做各种校园时间计算,计算学期,学年,第几周,周几
    msg7086
        7
    msg7086  
       2018-02-11 11:13:39 +08:00
    @cz5424 schooldate().get_term().weeks()
    leavic
        8
    leavic  
       2018-02-11 11:17:40 +08:00
    simple is better
    cz5424
        9
    cz5424  
    OP
       2018-02-11 11:30:12 +08:00 via Android
    @msg7086 如果用链式,这样的缺点是每次获取都要计算一次吧,他要获取的往往并不止一个参数,也就是得到周几的时候还要知道这一周是第几周
    msg7086
        10
    msg7086  
       2018-02-11 11:31:46 +08:00
    @cz5424 参考#1 最后一段内容。
    没有性能要求的时候,随便你怎么写。甚至越简单越好,因为越简单的代码,越容易读懂和针对性优化。
    ipwx
        11
    ipwx  
       2018-02-11 11:51:19 +08:00
    永远不要让外部调用者接触类内部的缓存策略。

    第一个版版直接枪毙。

    第二个版本,_get_term 命名不合理。get_XXX 一般默认不引起副作用,哪怕是私有(保护)函数。

    p.s. 为啥第二个版本不初始化 now_weeks = what_day = None,通过 is None 决定是否调用 _get_term,而要一个单独的 __get 标志位?
    ipwx
        12
    ipwx  
       2018-02-11 11:58:35 +08:00
    我觉得楼主和你的小伙伴最好注意一下“副作用”有关的各种约定。

    面向对象设计中,为了让调用更安全,函数语义上最好隔离“读取”和“写入”。换句话说,叫做 get_xxx 的,语义上应该只返回状态而不更改状态,叫做 set_xxx 的则只更改状态而不返回状态(返回 self 例外,这是链式调用)。

    缓存策略是另一回事。它应该对调用者透明。换句话说,你要是不高兴现在的缓存策略,可以随时更换,而不影响接口语义。所以你第一个版本是不对的,因为调用者需要明确知道 get_term 引起一次缓存,这个接口设计是不合理的。
    fxxkgw
        13
    fxxkgw  
       2018-02-11 12:16:12 +08:00
    第一个版本属性 weeks 和方法 weeks 难道不冲突么? SchoolDate object 加入叫 t,那 t.weeks t.weeks() 都能 OK 么?
    cz5424
        14
    cz5424  
    OP
       2018-02-11 12:26:23 +08:00 via Android
    @fxxkgw 不会的,第二个版本会冲突所以我改了,第一个版本是函数,第二个版本是参数
    cz5424
        15
    cz5424  
    OP
       2018-02-11 12:28:20 +08:00 via Android
    @ipwx 感谢,😂😂我就觉得一定要知道有 get_term 才能用其他函数问题很大
    paicha
        16
    paicha  
       2018-02-11 14:54:32 +08:00 via iPhone
    ipwx 说的已经挺多的,结构上第二种会比较好,还有命名写法的细节可以改进。

    其实没什么好争论的,Python 已经是有相当多最佳实践的例子了,可以阅读学习一些优秀开源项目代码。
    hsuan
        17
    hsuan  
       2018-02-11 15:13:28 +08:00 via Android
    两个都不咋地,好的代码要做到自解释,看到就应该明白是做什么的,该怎么用。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3265 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 14:15 · PVG 22:15 · LAX 07:15 · JFK 10:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.