V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
cz5424
V2EX  ›  问与答

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

  •  
  •   cz5424 · Feb 11, 2018 · 2730 views
    This topic created in 2999 days ago, the information mentioned may be changed or developed.

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

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

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

    第一个版版直接枪毙。

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

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

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

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

    其实没什么好争论的,Python 已经是有相当多最佳实践的例子了,可以阅读学习一些优秀开源项目代码。
    hsuan
        17
    hsuan  
       Feb 11, 2018 via Android
    两个都不咋地,好的代码要做到自解释,看到就应该明白是做什么的,该怎么用。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1454 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 16:54 · PVG 00:54 · LAX 09:54 · JFK 12:54
    ♥ Do have faith in what you're doing.