简体   繁体   中英

How to use collections.defaultdict, to ensure a straightforward comparison of values in Python

Basically this is a code modification of a previous question i saw on stack-overflow & I found it interesting to pursue which deals with port scanning . In this code I am trying to compare 2 pickle files which hold the scan result of 2 scans performed one after another. I am interested in finding

3 operations on the set of ports

 & (intersection): to see which ports have remained constant across scans (same ports) old - new: to see which ports were in the old scan but no longer in the new (deleted ports) new - old: to see which ports are in the new scan but were not in the old (added ports) 
def comp_ports(self,filename):
  try:
        f = open(filename)
        self.prev_report = pickle.load(f) # NmapReport

        for s in self.prev_report.hosts:
            self.old_port_dict[s.address] = collections.defaultdict(set())
            for x in s.get_open_ports():
                self.old_port_dict[s.address].add(x)

        for s in self.report.hosts:
            self.new_port_dict[s.address] = collections.defaultdict(set())
            for x in s.get_open_ports():
               self.new_port_dict[s.address].add(x)

        hosts = sorted(set(self.prev_report.hosts.keys() + self.report.hosts.keys()))

        for host in hosts:
                scan_same[host] = self.prev_report.hosts[host] & self.report.hosts[host]
                scan_new[host]  = self.report.hosts[host] - self.prev_report.hosts[host]
                scan_del[host]  = self.prev_report.hosts[host] - self.report.hosts[host]

        print()
        print('-' * 10, 'Same')
        for host, ports in scan_same.items():
            print(host, ':')
            for port in ports:
                 print(':::', port[0], '/', port[1])

        print()
        print('*' * 10, 'Added')
        for host, ports in scan_new.items():
            print(host, ':')
            for port in ports:
                  print(':::', port[0], '/', port[1])

        print()
        print('=' * 10, 'Deleted')
        for host, ports in scan_del.items():
            print(host, ':')
            for port in ports:
                   print(':::', port[0], '/', port[1])

  except Exception as l:
         print l

But the code throws :

first argument must be callable

Help me to use collection efficiently.

PS : Trying to improve this way How to compare dictionaries and see what changed?

You need to pass a callable (function / class / method) which will return a default value, not a default value itself.

Follow expression:

collections.defaultdict(set())

should be replaced with:

collections.defaultdict(set)

UPDATE

You need to create defaultdict s outside of the for loops:

self.old_port_dict = collections.defaultdict(set)
for s in self.prev_report.hosts:
    for x in s.get_open_ports():
        self.old_port_dict[s.address].add(x)

self.new_port_dict = collections.defaultdict(set)
for s in self.report.hosts:
    for x in s.get_open_ports():
        self.new_port_dict[s.address].add(x)

To answer the question in your title, you need to do

collections.defaultdict(set)

You need to pass in something that can be instantiated. Passing in collections.defaultdict(set()) is passing in something that already is instantiated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM