Как использовать декоратор и свойство для эффективной проверки типа и диапазона атрибутов класса? - PullRequest
0 голосов
/ 16 мая 2018
import math
import random

# === Problem 1

class RectangularRoom(object):
"""
A RectangularRoom represents a rectangular region containing clean or dirty
tiles.

A room has a width and a height and contains (width * height) tiles. Each tile
has some fixed amount of dirt. The tile is considered clean only when the amount
of dirt on this tile is 0.
"""

    def __init__(self, width, height, dirt_amount):
        """
        Initializes a rectangular room with the specified width, height, and 
        dirt_amount on each tile.

        width: an integer > 0
        height: an integer > 0
        dirt_amount: an integer >= 0
        """

        self.width, self.height, self.dirt_amount = width, height, dirt_amount
        tiles = [(w,h) for w in range(width) for h in range(height)]
        self.room = {tile:dirt for tile in tiles for dirt in [dirt_amount]}
        #raise NotImplementedError

    def get_width(self):

        return self._width

    def set_width(self, value):
        if value <= 0 :
            raise ValueError("Must be greater than 0")
        self._width = value

     width = property(get_width,set_width)

    def __str__(self):
        return str((self.room))

Это то, что я до сих пор делал с этим комнатным объектом.Я пытаюсь сделать высоту, dirt_amount также ограничен int и либо больше нуля или больше и равен нулю.Есть ли более простой или более эффективный способ кодирования этих ограничений для двух других атрибутов?

1 Ответ

0 голосов
/ 02 июня 2018
import types
from functools import wraps


def range_check(func):
    code = func.__code__
    allargs = list(code.co_varnames[:code.co_argcount])
    @wraps
    def _decorator(*args, **kargs):
        positionals = allargs[:len(args)]
        for argname, check in func.__annotations__.items():
            if argname in kargs:
                if not check(kargs[argname]):
                    raise TypeError('"%s" check failed' % argname)
            elif argname in positionals:
                pos = positionals.index(argname)
                if not check(args[pos]):
                    raise TypeError('"%s" check failed' % argname)
            else:
                # argument defaulted
                pass
        return func(*args, **kargs)
    return _decorator

def range(ratio):
    return 0 <= ratio and ratio <= 0.5

class Employee:
    def __init__(self, name, age, pay):
        self.name = name
        self.age = age
        self.pay = pay
    @range_check
    def giveRaise(self, ratio: range):
        self.pay *= (1 + ratio)

if __name__ == '__main__':
    alice = Employee('alice', 20, 50000)
    alice.giveRaise(0.1)
    alice.giveRaise(0.6)

Это пример кода для проверки аргументов, переданных функции.
1. используйте __annotations__ в python3 для передачи проверочных функций в объявлении функции.
2. используйте 'co_varnames [co_argcount]'чтобы получить список имен аргументов.
3. позиционные аргументы всегда помещаются перед аргументами ключевых слов.

...