Is passing too many arguments to the constructor considered an anti-pattern?












5















I am considering using the factory_boy library for API testing. An example from the documentation is:



class UserFactory(factory.Factory):
class Meta:
model = base.User

first_name = "John"
last_name = "Doe"


For this to work, we need first_name, last_name, etc to be passed as parameters to the __init__() method of the base.User() class. However, if you have many parameters this leads to something like:



class User(object):

GENDER_MALE = 'mr'
GENDER_FEMALE = 'ms'

def __init__(self, title=None, first_name=None, last_name=None, is_guest=None,
company_name=None, mobile=None, landline=None, email=None, password=None,
fax=None, wants_sms_notification=None, wants_email_notification=None,
wants_newsletter=None, street_address=None):

self. title = title
self.first_name = first_name
self.last_name = last_name
self.company_name = company_name
self.mobile = mobile
self.landline = landline
self.email = email
self.password = password
self.fax = fax
self.is_guest = is_guest
self.wants_sms_notification = wants_sms_notification
self.wants_email_notification = wants_email_notification
self.wants_newsletter = wants_newsletter
self.company_name = company_name
self.street_address = street_address


Now the question is, is this construction considered anti-pattern, and if yes, what alternatives do I have?



Thanks










share|improve this question

























  • You don't need an __init__ () method on your class to use factory_boy unless it's changed since v2.4.1.

    – kylie.a
    Jun 2 '15 at 15:01






  • 2





    This isn't an answer, but your issue could be a sign that the User class is trying to do too many things - ideally, you wouldn't want to have to modify the User class if you add, say, notifications over carrier pidgeon. It could also be a sign that its data model can be simplified - you could have a list of phone/fax numbers instead of mobile, landline and fax fields, make guest users a subclass instead of a field, and so on.

    – Wander Nauta
    Jun 2 '15 at 15:04






  • 2





    Maybe this is pedantic, but in python the phrase positional argument is defined as not being a keyword argument. Aside from the obligatory self you have no positional arguments in this __init__ method. No one needs to worry about if mobile goes in position 10 or 14 since it is specified by keyword.

    – Eric Appelt
    Jun 2 '15 at 15:08






  • 2





    You could also use def __init__(self, **kwargs): self.name=kwargs.get('name', None)

    – kylie.a
    Jun 2 '15 at 15:08








  • 1





    If you igonore for a moment the desirability or not of having all this contact fields in the table, with possible maintenance hassles, what is wrong with the code? These are optional fields so your call will populate as few as them as necessary (which Essence is expressly set to disallow). The intent'is clear. A Pattern would likely make code less clear and still not help with table maintenance. A kwargs approach is nice but hides your field names (what happens if you pass in firstname).

    – JL Peyret
    Jun 2 '15 at 15:18


















5















I am considering using the factory_boy library for API testing. An example from the documentation is:



class UserFactory(factory.Factory):
class Meta:
model = base.User

first_name = "John"
last_name = "Doe"


For this to work, we need first_name, last_name, etc to be passed as parameters to the __init__() method of the base.User() class. However, if you have many parameters this leads to something like:



class User(object):

GENDER_MALE = 'mr'
GENDER_FEMALE = 'ms'

def __init__(self, title=None, first_name=None, last_name=None, is_guest=None,
company_name=None, mobile=None, landline=None, email=None, password=None,
fax=None, wants_sms_notification=None, wants_email_notification=None,
wants_newsletter=None, street_address=None):

self. title = title
self.first_name = first_name
self.last_name = last_name
self.company_name = company_name
self.mobile = mobile
self.landline = landline
self.email = email
self.password = password
self.fax = fax
self.is_guest = is_guest
self.wants_sms_notification = wants_sms_notification
self.wants_email_notification = wants_email_notification
self.wants_newsletter = wants_newsletter
self.company_name = company_name
self.street_address = street_address


Now the question is, is this construction considered anti-pattern, and if yes, what alternatives do I have?



Thanks










share|improve this question

























  • You don't need an __init__ () method on your class to use factory_boy unless it's changed since v2.4.1.

    – kylie.a
    Jun 2 '15 at 15:01






  • 2





    This isn't an answer, but your issue could be a sign that the User class is trying to do too many things - ideally, you wouldn't want to have to modify the User class if you add, say, notifications over carrier pidgeon. It could also be a sign that its data model can be simplified - you could have a list of phone/fax numbers instead of mobile, landline and fax fields, make guest users a subclass instead of a field, and so on.

    – Wander Nauta
    Jun 2 '15 at 15:04






  • 2





    Maybe this is pedantic, but in python the phrase positional argument is defined as not being a keyword argument. Aside from the obligatory self you have no positional arguments in this __init__ method. No one needs to worry about if mobile goes in position 10 or 14 since it is specified by keyword.

    – Eric Appelt
    Jun 2 '15 at 15:08






  • 2





    You could also use def __init__(self, **kwargs): self.name=kwargs.get('name', None)

    – kylie.a
    Jun 2 '15 at 15:08








  • 1





    If you igonore for a moment the desirability or not of having all this contact fields in the table, with possible maintenance hassles, what is wrong with the code? These are optional fields so your call will populate as few as them as necessary (which Essence is expressly set to disallow). The intent'is clear. A Pattern would likely make code less clear and still not help with table maintenance. A kwargs approach is nice but hides your field names (what happens if you pass in firstname).

    – JL Peyret
    Jun 2 '15 at 15:18
















5












5








5


1






I am considering using the factory_boy library for API testing. An example from the documentation is:



class UserFactory(factory.Factory):
class Meta:
model = base.User

first_name = "John"
last_name = "Doe"


For this to work, we need first_name, last_name, etc to be passed as parameters to the __init__() method of the base.User() class. However, if you have many parameters this leads to something like:



class User(object):

GENDER_MALE = 'mr'
GENDER_FEMALE = 'ms'

def __init__(self, title=None, first_name=None, last_name=None, is_guest=None,
company_name=None, mobile=None, landline=None, email=None, password=None,
fax=None, wants_sms_notification=None, wants_email_notification=None,
wants_newsletter=None, street_address=None):

self. title = title
self.first_name = first_name
self.last_name = last_name
self.company_name = company_name
self.mobile = mobile
self.landline = landline
self.email = email
self.password = password
self.fax = fax
self.is_guest = is_guest
self.wants_sms_notification = wants_sms_notification
self.wants_email_notification = wants_email_notification
self.wants_newsletter = wants_newsletter
self.company_name = company_name
self.street_address = street_address


Now the question is, is this construction considered anti-pattern, and if yes, what alternatives do I have?



Thanks










share|improve this question
















I am considering using the factory_boy library for API testing. An example from the documentation is:



class UserFactory(factory.Factory):
class Meta:
model = base.User

first_name = "John"
last_name = "Doe"


For this to work, we need first_name, last_name, etc to be passed as parameters to the __init__() method of the base.User() class. However, if you have many parameters this leads to something like:



class User(object):

GENDER_MALE = 'mr'
GENDER_FEMALE = 'ms'

def __init__(self, title=None, first_name=None, last_name=None, is_guest=None,
company_name=None, mobile=None, landline=None, email=None, password=None,
fax=None, wants_sms_notification=None, wants_email_notification=None,
wants_newsletter=None, street_address=None):

self. title = title
self.first_name = first_name
self.last_name = last_name
self.company_name = company_name
self.mobile = mobile
self.landline = landline
self.email = email
self.password = password
self.fax = fax
self.is_guest = is_guest
self.wants_sms_notification = wants_sms_notification
self.wants_email_notification = wants_email_notification
self.wants_newsletter = wants_newsletter
self.company_name = company_name
self.street_address = street_address


Now the question is, is this construction considered anti-pattern, and if yes, what alternatives do I have?



Thanks







python factory-boy






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jun 2 '15 at 15:10







skamsie

















asked Jun 2 '15 at 14:51









skamsieskamsie

1,41221735




1,41221735













  • You don't need an __init__ () method on your class to use factory_boy unless it's changed since v2.4.1.

    – kylie.a
    Jun 2 '15 at 15:01






  • 2





    This isn't an answer, but your issue could be a sign that the User class is trying to do too many things - ideally, you wouldn't want to have to modify the User class if you add, say, notifications over carrier pidgeon. It could also be a sign that its data model can be simplified - you could have a list of phone/fax numbers instead of mobile, landline and fax fields, make guest users a subclass instead of a field, and so on.

    – Wander Nauta
    Jun 2 '15 at 15:04






  • 2





    Maybe this is pedantic, but in python the phrase positional argument is defined as not being a keyword argument. Aside from the obligatory self you have no positional arguments in this __init__ method. No one needs to worry about if mobile goes in position 10 or 14 since it is specified by keyword.

    – Eric Appelt
    Jun 2 '15 at 15:08






  • 2





    You could also use def __init__(self, **kwargs): self.name=kwargs.get('name', None)

    – kylie.a
    Jun 2 '15 at 15:08








  • 1





    If you igonore for a moment the desirability or not of having all this contact fields in the table, with possible maintenance hassles, what is wrong with the code? These are optional fields so your call will populate as few as them as necessary (which Essence is expressly set to disallow). The intent'is clear. A Pattern would likely make code less clear and still not help with table maintenance. A kwargs approach is nice but hides your field names (what happens if you pass in firstname).

    – JL Peyret
    Jun 2 '15 at 15:18





















  • You don't need an __init__ () method on your class to use factory_boy unless it's changed since v2.4.1.

    – kylie.a
    Jun 2 '15 at 15:01






  • 2





    This isn't an answer, but your issue could be a sign that the User class is trying to do too many things - ideally, you wouldn't want to have to modify the User class if you add, say, notifications over carrier pidgeon. It could also be a sign that its data model can be simplified - you could have a list of phone/fax numbers instead of mobile, landline and fax fields, make guest users a subclass instead of a field, and so on.

    – Wander Nauta
    Jun 2 '15 at 15:04






  • 2





    Maybe this is pedantic, but in python the phrase positional argument is defined as not being a keyword argument. Aside from the obligatory self you have no positional arguments in this __init__ method. No one needs to worry about if mobile goes in position 10 or 14 since it is specified by keyword.

    – Eric Appelt
    Jun 2 '15 at 15:08






  • 2





    You could also use def __init__(self, **kwargs): self.name=kwargs.get('name', None)

    – kylie.a
    Jun 2 '15 at 15:08








  • 1





    If you igonore for a moment the desirability or not of having all this contact fields in the table, with possible maintenance hassles, what is wrong with the code? These are optional fields so your call will populate as few as them as necessary (which Essence is expressly set to disallow). The intent'is clear. A Pattern would likely make code less clear and still not help with table maintenance. A kwargs approach is nice but hides your field names (what happens if you pass in firstname).

    – JL Peyret
    Jun 2 '15 at 15:18



















You don't need an __init__ () method on your class to use factory_boy unless it's changed since v2.4.1.

– kylie.a
Jun 2 '15 at 15:01





You don't need an __init__ () method on your class to use factory_boy unless it's changed since v2.4.1.

– kylie.a
Jun 2 '15 at 15:01




2




2





This isn't an answer, but your issue could be a sign that the User class is trying to do too many things - ideally, you wouldn't want to have to modify the User class if you add, say, notifications over carrier pidgeon. It could also be a sign that its data model can be simplified - you could have a list of phone/fax numbers instead of mobile, landline and fax fields, make guest users a subclass instead of a field, and so on.

– Wander Nauta
Jun 2 '15 at 15:04





This isn't an answer, but your issue could be a sign that the User class is trying to do too many things - ideally, you wouldn't want to have to modify the User class if you add, say, notifications over carrier pidgeon. It could also be a sign that its data model can be simplified - you could have a list of phone/fax numbers instead of mobile, landline and fax fields, make guest users a subclass instead of a field, and so on.

– Wander Nauta
Jun 2 '15 at 15:04




2




2





Maybe this is pedantic, but in python the phrase positional argument is defined as not being a keyword argument. Aside from the obligatory self you have no positional arguments in this __init__ method. No one needs to worry about if mobile goes in position 10 or 14 since it is specified by keyword.

– Eric Appelt
Jun 2 '15 at 15:08





Maybe this is pedantic, but in python the phrase positional argument is defined as not being a keyword argument. Aside from the obligatory self you have no positional arguments in this __init__ method. No one needs to worry about if mobile goes in position 10 or 14 since it is specified by keyword.

– Eric Appelt
Jun 2 '15 at 15:08




2




2





You could also use def __init__(self, **kwargs): self.name=kwargs.get('name', None)

– kylie.a
Jun 2 '15 at 15:08







You could also use def __init__(self, **kwargs): self.name=kwargs.get('name', None)

– kylie.a
Jun 2 '15 at 15:08






1




1





If you igonore for a moment the desirability or not of having all this contact fields in the table, with possible maintenance hassles, what is wrong with the code? These are optional fields so your call will populate as few as them as necessary (which Essence is expressly set to disallow). The intent'is clear. A Pattern would likely make code less clear and still not help with table maintenance. A kwargs approach is nice but hides your field names (what happens if you pass in firstname).

– JL Peyret
Jun 2 '15 at 15:18







If you igonore for a moment the desirability or not of having all this contact fields in the table, with possible maintenance hassles, what is wrong with the code? These are optional fields so your call will populate as few as them as necessary (which Essence is expressly set to disallow). The intent'is clear. A Pattern would likely make code less clear and still not help with table maintenance. A kwargs approach is nice but hides your field names (what happens if you pass in firstname).

– JL Peyret
Jun 2 '15 at 15:18














4 Answers
4






active

oldest

votes


















7














You could pack the __init__ method's keyword arguments into one dict, and alter the instance's __dict__ directly:



class User(object):
GENDER_MALE = 'mr'
GENDER_FEMALE = 'ms'
def __init__(self, **kwargs):
valid_keys = ["title", "first_name", "last_name", "is_guest", "company_name", "mobile", "landline", "email", "password", "fax", "wants_sms_notification", "wants_email_notification", "wants_newsletter","street_address"]
for key in valid_keys:
self.__dict__[key] = kwargs.get(key)

x = User(first_name="Kevin", password="hunter2")
print x.first_name, x.password, x.mobile


However, this has the drawback of disallowing you from supplying arguments without naming them - x = User("Mr", "Kevin") works with your original code, but not with this code.






share|improve this answer
























  • I prefer this solution, but you loose the code completion. Is there a way not to?

    – jankos
    Jun 2 '15 at 22:08











  • +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

    – ElmoVanKielmo
    Jun 3 '15 at 11:28



















3














In Python 3.7, dataclasses (specified in PEP557) were added. This allows you to only write these arguments once and not again in the constructor, since the constructor is made for you:



from dataclasses import dataclass

@dataclass
class User:
title: str = None
first_name: str = None
last_name: str = None
company_name: str = None
mobile: str = None
landline: str = None
email: str = None
password: str = None
fax: str = None
is_guest: bool = True
wants_sms_notification: bool = False
wants_email_notification: bool = False
wants_newsletter: bool = False
street_address: str = None


It also adds a __repr__ to the class as well as some others. Note that explicitly inheriting from object is no longer needed in Python 3, since all classes are new-style classes by default.



There are a few drawbacks, though. It is slightly slower on class definition (since these methods need to be generated). You need to either set a default value or add a type annotation, otherwise you get a name error. If you want to use a mutable object, like a list, as a default argument, you need to use dataclass.field(default_factory=list) (normally it is discouraged to write e.g. def f(x=), but here it actually raises an exception).






share|improve this answer

































    2














    Yes, too many arguments is an antipattern (as stated in Clean Code by RObert C. Martin)



    To avoid this, you have two design approaches:



    The essence pattern



    The fluent interface/builder pattern



    These are both similar in intent, in that we slowly build up an intermediate object, and then create our target object in a single step.



    I'd recommend the builder pattern, it makes the code easy to read.






    share|improve this answer
























    • So is an "essence" the same thing as a "parameter object" for a constructor?

      – Damian Yerrick
      Jun 2 '15 at 15:48






    • 1





      Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

      – Dave Kielpinski
      Sep 7 '17 at 18:00





















    1














    The biggest risk would be if you had a large number of positional arguments and then ended up not knowing which is which.. Keyword arguments definitely make this better.



    As suggested by others, the builder pattern also works quite nicely.
    If you have a very large number of fields, you can also do something more generic, like so:



    class Builder(object):

    def __init__(self, cls):
    self.attrs = {}
    self.cls = cls

    def __getattr__(self, name):
    if name[0:3] == 'set':
    def setter(x):
    field_name = name[3].lower() + name[4:]
    self.attrs[field_name] = x
    return self
    return setter
    else:
    return super(UserBuilder, self).__getattribute__(name)

    def build(self):
    return self.cls(**self.attrs)

    class User(object):

    def __str__(self):
    return "%s %s" % (self.firstName, self.lastName)

    def __init__(self, **kwargs):
    # TODO: validate fields
    for key in kwargs:
    setattr(self, key, kwargs[key])

    @classmethod
    def builder(cls):
    return Builder(cls)

    print (User.builder()
    .setFirstName('John')
    .setLastName('Doe')
    .build()) # prints John Doe





    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f30599478%2fis-passing-too-many-arguments-to-the-constructor-considered-an-anti-pattern%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      7














      You could pack the __init__ method's keyword arguments into one dict, and alter the instance's __dict__ directly:



      class User(object):
      GENDER_MALE = 'mr'
      GENDER_FEMALE = 'ms'
      def __init__(self, **kwargs):
      valid_keys = ["title", "first_name", "last_name", "is_guest", "company_name", "mobile", "landline", "email", "password", "fax", "wants_sms_notification", "wants_email_notification", "wants_newsletter","street_address"]
      for key in valid_keys:
      self.__dict__[key] = kwargs.get(key)

      x = User(first_name="Kevin", password="hunter2")
      print x.first_name, x.password, x.mobile


      However, this has the drawback of disallowing you from supplying arguments without naming them - x = User("Mr", "Kevin") works with your original code, but not with this code.






      share|improve this answer
























      • I prefer this solution, but you loose the code completion. Is there a way not to?

        – jankos
        Jun 2 '15 at 22:08











      • +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

        – ElmoVanKielmo
        Jun 3 '15 at 11:28
















      7














      You could pack the __init__ method's keyword arguments into one dict, and alter the instance's __dict__ directly:



      class User(object):
      GENDER_MALE = 'mr'
      GENDER_FEMALE = 'ms'
      def __init__(self, **kwargs):
      valid_keys = ["title", "first_name", "last_name", "is_guest", "company_name", "mobile", "landline", "email", "password", "fax", "wants_sms_notification", "wants_email_notification", "wants_newsletter","street_address"]
      for key in valid_keys:
      self.__dict__[key] = kwargs.get(key)

      x = User(first_name="Kevin", password="hunter2")
      print x.first_name, x.password, x.mobile


      However, this has the drawback of disallowing you from supplying arguments without naming them - x = User("Mr", "Kevin") works with your original code, but not with this code.






      share|improve this answer
























      • I prefer this solution, but you loose the code completion. Is there a way not to?

        – jankos
        Jun 2 '15 at 22:08











      • +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

        – ElmoVanKielmo
        Jun 3 '15 at 11:28














      7












      7








      7







      You could pack the __init__ method's keyword arguments into one dict, and alter the instance's __dict__ directly:



      class User(object):
      GENDER_MALE = 'mr'
      GENDER_FEMALE = 'ms'
      def __init__(self, **kwargs):
      valid_keys = ["title", "first_name", "last_name", "is_guest", "company_name", "mobile", "landline", "email", "password", "fax", "wants_sms_notification", "wants_email_notification", "wants_newsletter","street_address"]
      for key in valid_keys:
      self.__dict__[key] = kwargs.get(key)

      x = User(first_name="Kevin", password="hunter2")
      print x.first_name, x.password, x.mobile


      However, this has the drawback of disallowing you from supplying arguments without naming them - x = User("Mr", "Kevin") works with your original code, but not with this code.






      share|improve this answer













      You could pack the __init__ method's keyword arguments into one dict, and alter the instance's __dict__ directly:



      class User(object):
      GENDER_MALE = 'mr'
      GENDER_FEMALE = 'ms'
      def __init__(self, **kwargs):
      valid_keys = ["title", "first_name", "last_name", "is_guest", "company_name", "mobile", "landline", "email", "password", "fax", "wants_sms_notification", "wants_email_notification", "wants_newsletter","street_address"]
      for key in valid_keys:
      self.__dict__[key] = kwargs.get(key)

      x = User(first_name="Kevin", password="hunter2")
      print x.first_name, x.password, x.mobile


      However, this has the drawback of disallowing you from supplying arguments without naming them - x = User("Mr", "Kevin") works with your original code, but not with this code.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Jun 2 '15 at 15:08









      KevinKevin

      59.2k1169113




      59.2k1169113













      • I prefer this solution, but you loose the code completion. Is there a way not to?

        – jankos
        Jun 2 '15 at 22:08











      • +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

        – ElmoVanKielmo
        Jun 3 '15 at 11:28



















      • I prefer this solution, but you loose the code completion. Is there a way not to?

        – jankos
        Jun 2 '15 at 22:08











      • +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

        – ElmoVanKielmo
        Jun 3 '15 at 11:28

















      I prefer this solution, but you loose the code completion. Is there a way not to?

      – jankos
      Jun 2 '15 at 22:08





      I prefer this solution, but you loose the code completion. Is there a way not to?

      – jankos
      Jun 2 '15 at 22:08













      +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

      – ElmoVanKielmo
      Jun 3 '15 at 11:28





      +1 but for completeness, you should throw TypeError if keyword argument was passed and it's not in valid_keys list.

      – ElmoVanKielmo
      Jun 3 '15 at 11:28













      3














      In Python 3.7, dataclasses (specified in PEP557) were added. This allows you to only write these arguments once and not again in the constructor, since the constructor is made for you:



      from dataclasses import dataclass

      @dataclass
      class User:
      title: str = None
      first_name: str = None
      last_name: str = None
      company_name: str = None
      mobile: str = None
      landline: str = None
      email: str = None
      password: str = None
      fax: str = None
      is_guest: bool = True
      wants_sms_notification: bool = False
      wants_email_notification: bool = False
      wants_newsletter: bool = False
      street_address: str = None


      It also adds a __repr__ to the class as well as some others. Note that explicitly inheriting from object is no longer needed in Python 3, since all classes are new-style classes by default.



      There are a few drawbacks, though. It is slightly slower on class definition (since these methods need to be generated). You need to either set a default value or add a type annotation, otherwise you get a name error. If you want to use a mutable object, like a list, as a default argument, you need to use dataclass.field(default_factory=list) (normally it is discouraged to write e.g. def f(x=), but here it actually raises an exception).






      share|improve this answer






























        3














        In Python 3.7, dataclasses (specified in PEP557) were added. This allows you to only write these arguments once and not again in the constructor, since the constructor is made for you:



        from dataclasses import dataclass

        @dataclass
        class User:
        title: str = None
        first_name: str = None
        last_name: str = None
        company_name: str = None
        mobile: str = None
        landline: str = None
        email: str = None
        password: str = None
        fax: str = None
        is_guest: bool = True
        wants_sms_notification: bool = False
        wants_email_notification: bool = False
        wants_newsletter: bool = False
        street_address: str = None


        It also adds a __repr__ to the class as well as some others. Note that explicitly inheriting from object is no longer needed in Python 3, since all classes are new-style classes by default.



        There are a few drawbacks, though. It is slightly slower on class definition (since these methods need to be generated). You need to either set a default value or add a type annotation, otherwise you get a name error. If you want to use a mutable object, like a list, as a default argument, you need to use dataclass.field(default_factory=list) (normally it is discouraged to write e.g. def f(x=), but here it actually raises an exception).






        share|improve this answer




























          3












          3








          3







          In Python 3.7, dataclasses (specified in PEP557) were added. This allows you to only write these arguments once and not again in the constructor, since the constructor is made for you:



          from dataclasses import dataclass

          @dataclass
          class User:
          title: str = None
          first_name: str = None
          last_name: str = None
          company_name: str = None
          mobile: str = None
          landline: str = None
          email: str = None
          password: str = None
          fax: str = None
          is_guest: bool = True
          wants_sms_notification: bool = False
          wants_email_notification: bool = False
          wants_newsletter: bool = False
          street_address: str = None


          It also adds a __repr__ to the class as well as some others. Note that explicitly inheriting from object is no longer needed in Python 3, since all classes are new-style classes by default.



          There are a few drawbacks, though. It is slightly slower on class definition (since these methods need to be generated). You need to either set a default value or add a type annotation, otherwise you get a name error. If you want to use a mutable object, like a list, as a default argument, you need to use dataclass.field(default_factory=list) (normally it is discouraged to write e.g. def f(x=), but here it actually raises an exception).






          share|improve this answer















          In Python 3.7, dataclasses (specified in PEP557) were added. This allows you to only write these arguments once and not again in the constructor, since the constructor is made for you:



          from dataclasses import dataclass

          @dataclass
          class User:
          title: str = None
          first_name: str = None
          last_name: str = None
          company_name: str = None
          mobile: str = None
          landline: str = None
          email: str = None
          password: str = None
          fax: str = None
          is_guest: bool = True
          wants_sms_notification: bool = False
          wants_email_notification: bool = False
          wants_newsletter: bool = False
          street_address: str = None


          It also adds a __repr__ to the class as well as some others. Note that explicitly inheriting from object is no longer needed in Python 3, since all classes are new-style classes by default.



          There are a few drawbacks, though. It is slightly slower on class definition (since these methods need to be generated). You need to either set a default value or add a type annotation, otherwise you get a name error. If you want to use a mutable object, like a list, as a default argument, you need to use dataclass.field(default_factory=list) (normally it is discouraged to write e.g. def f(x=), but here it actually raises an exception).







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 1 at 16:20

























          answered Jan 1 at 16:14









          GraipherGraipher

          4,4441533




          4,4441533























              2














              Yes, too many arguments is an antipattern (as stated in Clean Code by RObert C. Martin)



              To avoid this, you have two design approaches:



              The essence pattern



              The fluent interface/builder pattern



              These are both similar in intent, in that we slowly build up an intermediate object, and then create our target object in a single step.



              I'd recommend the builder pattern, it makes the code easy to read.






              share|improve this answer
























              • So is an "essence" the same thing as a "parameter object" for a constructor?

                – Damian Yerrick
                Jun 2 '15 at 15:48






              • 1





                Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

                – Dave Kielpinski
                Sep 7 '17 at 18:00


















              2














              Yes, too many arguments is an antipattern (as stated in Clean Code by RObert C. Martin)



              To avoid this, you have two design approaches:



              The essence pattern



              The fluent interface/builder pattern



              These are both similar in intent, in that we slowly build up an intermediate object, and then create our target object in a single step.



              I'd recommend the builder pattern, it makes the code easy to read.






              share|improve this answer
























              • So is an "essence" the same thing as a "parameter object" for a constructor?

                – Damian Yerrick
                Jun 2 '15 at 15:48






              • 1





                Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

                – Dave Kielpinski
                Sep 7 '17 at 18:00
















              2












              2








              2







              Yes, too many arguments is an antipattern (as stated in Clean Code by RObert C. Martin)



              To avoid this, you have two design approaches:



              The essence pattern



              The fluent interface/builder pattern



              These are both similar in intent, in that we slowly build up an intermediate object, and then create our target object in a single step.



              I'd recommend the builder pattern, it makes the code easy to read.






              share|improve this answer













              Yes, too many arguments is an antipattern (as stated in Clean Code by RObert C. Martin)



              To avoid this, you have two design approaches:



              The essence pattern



              The fluent interface/builder pattern



              These are both similar in intent, in that we slowly build up an intermediate object, and then create our target object in a single step.



              I'd recommend the builder pattern, it makes the code easy to read.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Jun 2 '15 at 14:58









              EzzoredEzzored

              75239




              75239













              • So is an "essence" the same thing as a "parameter object" for a constructor?

                – Damian Yerrick
                Jun 2 '15 at 15:48






              • 1





                Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

                – Dave Kielpinski
                Sep 7 '17 at 18:00





















              • So is an "essence" the same thing as a "parameter object" for a constructor?

                – Damian Yerrick
                Jun 2 '15 at 15:48






              • 1





                Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

                – Dave Kielpinski
                Sep 7 '17 at 18:00



















              So is an "essence" the same thing as a "parameter object" for a constructor?

              – Damian Yerrick
              Jun 2 '15 at 15:48





              So is an "essence" the same thing as a "parameter object" for a constructor?

              – Damian Yerrick
              Jun 2 '15 at 15:48




              1




              1





              Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

              – Dave Kielpinski
              Sep 7 '17 at 18:00







              Second link is pretty useless since it includes a lot of jumping through Java-specific hoops. There's a good code example in Wikipedia en.wikipedia.org/wiki/Builder_pattern

              – Dave Kielpinski
              Sep 7 '17 at 18:00













              1














              The biggest risk would be if you had a large number of positional arguments and then ended up not knowing which is which.. Keyword arguments definitely make this better.



              As suggested by others, the builder pattern also works quite nicely.
              If you have a very large number of fields, you can also do something more generic, like so:



              class Builder(object):

              def __init__(self, cls):
              self.attrs = {}
              self.cls = cls

              def __getattr__(self, name):
              if name[0:3] == 'set':
              def setter(x):
              field_name = name[3].lower() + name[4:]
              self.attrs[field_name] = x
              return self
              return setter
              else:
              return super(UserBuilder, self).__getattribute__(name)

              def build(self):
              return self.cls(**self.attrs)

              class User(object):

              def __str__(self):
              return "%s %s" % (self.firstName, self.lastName)

              def __init__(self, **kwargs):
              # TODO: validate fields
              for key in kwargs:
              setattr(self, key, kwargs[key])

              @classmethod
              def builder(cls):
              return Builder(cls)

              print (User.builder()
              .setFirstName('John')
              .setLastName('Doe')
              .build()) # prints John Doe





              share|improve this answer




























                1














                The biggest risk would be if you had a large number of positional arguments and then ended up not knowing which is which.. Keyword arguments definitely make this better.



                As suggested by others, the builder pattern also works quite nicely.
                If you have a very large number of fields, you can also do something more generic, like so:



                class Builder(object):

                def __init__(self, cls):
                self.attrs = {}
                self.cls = cls

                def __getattr__(self, name):
                if name[0:3] == 'set':
                def setter(x):
                field_name = name[3].lower() + name[4:]
                self.attrs[field_name] = x
                return self
                return setter
                else:
                return super(UserBuilder, self).__getattribute__(name)

                def build(self):
                return self.cls(**self.attrs)

                class User(object):

                def __str__(self):
                return "%s %s" % (self.firstName, self.lastName)

                def __init__(self, **kwargs):
                # TODO: validate fields
                for key in kwargs:
                setattr(self, key, kwargs[key])

                @classmethod
                def builder(cls):
                return Builder(cls)

                print (User.builder()
                .setFirstName('John')
                .setLastName('Doe')
                .build()) # prints John Doe





                share|improve this answer


























                  1












                  1








                  1







                  The biggest risk would be if you had a large number of positional arguments and then ended up not knowing which is which.. Keyword arguments definitely make this better.



                  As suggested by others, the builder pattern also works quite nicely.
                  If you have a very large number of fields, you can also do something more generic, like so:



                  class Builder(object):

                  def __init__(self, cls):
                  self.attrs = {}
                  self.cls = cls

                  def __getattr__(self, name):
                  if name[0:3] == 'set':
                  def setter(x):
                  field_name = name[3].lower() + name[4:]
                  self.attrs[field_name] = x
                  return self
                  return setter
                  else:
                  return super(UserBuilder, self).__getattribute__(name)

                  def build(self):
                  return self.cls(**self.attrs)

                  class User(object):

                  def __str__(self):
                  return "%s %s" % (self.firstName, self.lastName)

                  def __init__(self, **kwargs):
                  # TODO: validate fields
                  for key in kwargs:
                  setattr(self, key, kwargs[key])

                  @classmethod
                  def builder(cls):
                  return Builder(cls)

                  print (User.builder()
                  .setFirstName('John')
                  .setLastName('Doe')
                  .build()) # prints John Doe





                  share|improve this answer













                  The biggest risk would be if you had a large number of positional arguments and then ended up not knowing which is which.. Keyword arguments definitely make this better.



                  As suggested by others, the builder pattern also works quite nicely.
                  If you have a very large number of fields, you can also do something more generic, like so:



                  class Builder(object):

                  def __init__(self, cls):
                  self.attrs = {}
                  self.cls = cls

                  def __getattr__(self, name):
                  if name[0:3] == 'set':
                  def setter(x):
                  field_name = name[3].lower() + name[4:]
                  self.attrs[field_name] = x
                  return self
                  return setter
                  else:
                  return super(UserBuilder, self).__getattribute__(name)

                  def build(self):
                  return self.cls(**self.attrs)

                  class User(object):

                  def __str__(self):
                  return "%s %s" % (self.firstName, self.lastName)

                  def __init__(self, **kwargs):
                  # TODO: validate fields
                  for key in kwargs:
                  setattr(self, key, kwargs[key])

                  @classmethod
                  def builder(cls):
                  return Builder(cls)

                  print (User.builder()
                  .setFirstName('John')
                  .setLastName('Doe')
                  .build()) # prints John Doe






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Jun 2 '15 at 15:31









                  VladVlad

                  15.3k43162




                  15.3k43162






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f30599478%2fis-passing-too-many-arguments-to-the-constructor-considered-an-anti-pattern%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      MongoDB - Not Authorized To Execute Command

                      in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith

                      Npm cannot find a required file even through it is in the searched directory