How to use a range for dict keys?

2019-03-02 17:05发布

问题:

I have a program that scans Google for links, it verifies how many links you've found and then tries to find a success right for your search:

def check_urls_for_queries(self):
    """ The returned URLS will be run through a query regex to see if
        they have a query parameter.
          http://google.com <- False
          http://example.com/php?id=2 <- True
    """
    filename = settings.create_random_filename()
    print("File being saved to: {}".format(filename))
    with open("{}\\{}.txt".format(settings.DORK_SCAN_RESULTS_PATH, filename),
              "a+") as results:
        for url in self.connect_to_search_engine():
            match = settings.QUERY_REGEX.match(url) # Match by regex for anything
                                                    # that has a ?=<PARAM> in it
            if match:
                results.write(url + "\n")
    amount_of_urls = len(open(settings.DORK_SCAN_RESULTS_PATH + "\\" + filename
                              + ".txt", 'r').readlines())
    return("\nFound a total of {} usable links with query (GET) parameters, urls"
           " have been saved to {}\\{}.txt. This Dork has a success rate of {}%".
           format(amount_of_urls, settings.DORK_SCAN_RESULTS_PATH, filename,
                  self.success_rate[amount_of_urls]
                    if amount_of_urls in self.success_rate.keys() else
                  'Unknown success rate'))

What I would like to do is create the success rate using a range per dict key:

success_rate = {
    range(1, 10): 10, range(11, 20): 20, range(21, 30): 30,
    range(31, 40): 40, range(41, 50): 50, range(51, 60): 60,
    range(61, 70): 70, range(71, 80): 80, range(81, 90): 90,
    range(91, 100): 100
}

However obviously this doesn't work because lists are not hashable objects:

Traceback (most recent call last):
  File "tool.py", line 2, in <module>
    from lib.core import BANNER
  File "C:\Users\Documents\bin\python\pybelt\lib\core\__init__.py", line 1, in <module>
    from dork_check import DorkScanner
  File "C:\Users\Documents\bin\python\pybelt\lib\core\dork_check\__init__.py", line 1, in <module>
    from dorks import DorkScanner
  File "C:\Users\\Documents\bin\python\pybelt\lib\core\dork_check\dorks.py", line 6, in <module>
    class DorkScanner(object):
  File "C:\Users\Documents\bin\python\pybelt\lib\core\dork_check\dorks.py", line 11, in DorkScanner
    range(1, 10): 10, range(11, 20): 20, range(21, 30): 30,
TypeError: unhashable type: 'list'

Is there a way I could use a range for a dict key to save myself from having to make a key from 1 – 100?

回答1:

range() in Python 2 is merely a function that returns a list of integers, it is not itself a type of object. And you wouldn't want to use lists here, no, because they contain all integers in the range.

You could use xrange() objects instead, these are hashable, and only store the start and end values. However, unless you plan to use other xrange() objects to test these keys, a dictionary with such keys is not very useful, you'd have to loop over the dictionary to test your rate against each xrange object manually.

Your success rate dictionary could more simply be replaced by maths; just round your numbers up to the nearest multiple of 10 (simply using floor division):

success_rate = ((amount_of_urls // 10) + 1) * 10

Do test if that resulting value is between 10 and 100:

10 <= success_rate <= 100


回答2:

RangeKeyDict class could be able to handle cases like this, which is more general and easy to use. For usage, check the codes in __main__

to install it using:

pip install range-key-dict

Usage:

from range_key_dict import RangeKeyDict

if __name__ == '__main__':
    range_key_dict = RangeKeyDict({
        (0, 100): 'A',
        (100, 200): 'B',
        (200, 300): 'C',
    })

    # test normal case
    assert range_key_dict[70] == 'A'
    assert range_key_dict[170] == 'B'
    assert range_key_dict[270] == 'C'

    # test case when the number is float
    assert range_key_dict[70.5] == 'A'

    # test case not in the range, with default value
    assert range_key_dict.get(1000, 'D') == 'D'

https://github.com/albertmenglongli/range-key-dict

Reference: Please check the answer in this post: Range as dictionary key in Python