0

I'm using a NamedTuple to define a server to which I want to connect using telnetlib. Then I created a class which defines the connection to the server, with the server details and connection method inside the class. Then outside of the class, I want to use the connection method with the server's NamedTuple as connection credentials. However I keep getting the Error that the connection method is missing the NamedTuple argument.

I've tried pulling the NamedTuple outside of the class, tried to put the Namedtuple inside the init method of the class. nothing seems to work.

This is my code:

import telnetlib
from typing import NamedTuple

class Unit(NamedTuple):
    name: str
    ip: str
    port: str

    def printunit(self, unit):
        print(unit.name)
        print(unit.ip)
        print(unit.port)

class TnCnct:
    Server1 = Unit("Server1", "1.1.1.1", "23")
    Server2 = Unit("Server2", "2.2.2.2", "23")
    Server3 = Unit("Server3", "3.3.3.3", "23")

    def __init__(self):
        pass

    def cnct(self, u):
        try:
            tn = telnetlib.Telnet(u.ip, u.port, 10)
            tn.open(u.ip, u.port)
            tn.close()
            response = u.name + " " + "Success!"
        except Exception as e:
            response = u.name + " " + "Failed!"
            print(e)
        finally:
            print(response)


TnCnct.cnct(TnCnct.Server1)

The exact Error I get:

TypeError: cnct() missing 1 required positional argument: 'u'
Alon
  • 99
  • 1
  • 9

2 Answers2

3

cnct is a method which require an object instance. Here you try to call it as a class method. If that's what you want, you should use a decorator:

    @classmethod
    def cnct(cls, u):
        ...
Francis Colas
  • 3,459
  • 2
  • 26
  • 31
  • Oh that worked great! I'm pretty much a begginner to Object_oriented Programming. Could you explain to me why that didn't work? Or how could've I made it work as an Object Instance method? – Alon Aug 04 '19 at 08:58
  • 1
    For the difference between instance and class method, see the second part of the answer from @patrick-artner – Francis Colas Aug 04 '19 at 09:46
  • 1
    Additionally, a class with a single method that doesn't use `self` is often a good candidate for being transformed into a function (but it's not a mantra, it's a design decision to think about). Cf. for instance: https://docs.quantifiedcode.com/python-anti-patterns/correctness/method_could_be_a_function.html – Francis Colas Aug 04 '19 at 13:33
3

1. You might want to use namedtuples from collections - not typing:

namedtuples:

Returns a new tuple subclass named typename. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with typename and field_names) and a helpful repr() method which lists the tuple contents in a name=value format.

vs typing:

This module supports type hints as specified by PEP 484 and PEP 526. The most fundamental support consists of the types Any, Union, Tuple, Callable, TypeVar, and Generic. For full specification please see PEP 484. For a simplified introduction to type hints see PEP 483.

The typing NamedTuples are just a wrapper around the original namedtuples.

2. You need instances to use instancemethods:

Fixes for both:

import telnetlib
from collections import namedtuple

def printunit(self, unit):
    print(unit.name)
    print(unit.ip)
    print(unit.port)

Unit = namedtuple("Unit","name ip port")
Unit.printunit = printunit 

class TnCnct:
    Server1 = Unit("Server1", "1.1.1.1", "23")
    Server2 = Unit("Server2", "2.2.2.2", "23")
    Server3 = Unit("Server3", "3.3.3.3", "23")

    def __init__(self):
        pass

    def cnct(self, u):
        try:
            tn = telnetlib.Telnet(u.ip, u.port, 10)
            tn.open(u.ip, u.port)
            tn.close()
            response = u.name + " " + "Success!"
        except Exception as e:
            response = u.name + " " + "Failed!"
            print(e)
        finally:
            print(response)

# create a class instance and use the cnct method of it 
connector = TnCnct()
connector.cnct(TnCnct.Server1)

The difference between instance and class (methods/variables) is detailed f.e. here:

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • 2
    It is perfectly valid to use `typing.NamedTuple` as OP did. Try it out. – Francis Colas Aug 04 '19 at 09:47
  • @FrancisColas sure it is ... look into [typing-sources](https://github.com/python/typing/blob/master/src/typing.py#L2179) - IT uses collections.namedtuple internally as well, it is just a wrapper around the collections.namedtuple - so why the indirection instead of using the correct module to begin with? – Patrick Artner Aug 04 '19 at 10:53
  • 1
    Well, that's the point of optional typing in Python, you use it to give type information (which is a good idea). If you don't want to do it, it's fine; but your original statement was “You *need* to use namedtuples from collections” (emphasis mine) which was neither the point of the question nor an actual improvement of his code. You corrected it into “might want” but it is still a distraction with respect to the rest of your answer, which is actually good. – Francis Colas Aug 04 '19 at 11:30