0

So I wrote a function called 'anonimize' that gets a string and returns that string with some details blacked-out, using regex. The function works fine on its own, but I tried to turn it into a decorator and things went south and got so many crashes so many times. I am learning decorators and would really like to understand what I am doing wrong. In this unsuccessful version I got "TypeError: BankApplication.anonimize() missing 1 required positional argument: 'callable'".

import re


class BankApplication:

    def __init__(self, bank_name):
        self.name = bank_name

    '''
      Perform anonimization to the text data:
      1. Remove possible account numbers (more than 5 digits in a row)
      2. Remove possible email addresses
      3. Remove possible First + Last names (two consequtive words 
         starting from upper case, but not divided by .)
   '''

    def anonimize(func):
        def inner(arg):
            ret = func(arg)
            print(
                re.sub("[A-Z][a-z]+\s[A-Z][a-z]+", "***", re.sub("\S+@\S+\.\S+", "***", re.sub("\d{5,}", "***", arg))))
            return ret

        return inner

    @anonimize
    def feedback(self, feedback_text):
        print("Called feedback")

    @anonimize
    def log_info(self, info_to_log):
        print("Called feedback")


bank = BankApplication("Bank")
bank.feedback("Name: John Doe\nE-mail: johndoe@fakemail.com\nAccount number: 911911911.")
swapman
  • 11
  • 2

3 Answers3

0

anonimize shouldn't be an instance method of BankApplication, it doesn't even accept self. Define it outside of the class.

EDIT: I've also suggested @staticmethod, but it wouldn't work either, see @James's answer.

Expurple
  • 877
  • 6
  • 13
  • and `arg` should be `*arg` where defined and `arg[1]` where used. – luk2302 Dec 30 '21 at 13:26
  • @luk2302, yeah, ideally decorator should `def inner(*args, **kwargs)`, but I'm not sure if I agree with you in this case. It seems like the decorator is designed for these one-argument methods specifically, and not for general use. `arg[1]` wouldn't work in the general case anyway, because it assumes that the string parameter always comes first. If I were the OP, I would simply apply a string-transforming function to the argument inside of these methods, instead of decorating them. Still one line, but simpler. – Expurple Dec 30 '21 at 13:32
0

You need to move anonimize out of the class. Why?

The first argument that gets passed to a standard method is always the owning object itself (the self argument is the standard syntax, but you can call it anything).

You cannot use it as a static method because decorators are run when the code is defined. You won't be able to reference the owning object because it won't have been instantiated yet.

import re

def anonimize(func):
    def inner(arg):
        ret = func(arg)
        print(
            re.sub("[A-Z][a-z]+\s[A-Z][a-z]+", "***", re.sub("\S+@\S+\.\S+", "***", re.sub("\d{5,}", "***", arg))))
        return ret
    return inner

class BankApplication:

    def __init__(self, bank_name):
        self.name = bank_name

    '''
      Perform anonimization to the text data:
      1. Remove possible account numbers (more than 5 digits in a row)
      2. Remove possible email addresses
      3. Remove possible First + Last names (two consequtive words 
         starting from upper case, but not divided by .)
   '''

    ...
James
  • 32,991
  • 4
  • 47
  • 70
  • Interesting note about `@staticmethod`, I didn't think about it. I'll edit my answer – Expurple Dec 30 '21 at 13:44
  • thanks :) I thought that as well, this was the way the teacher wanted us to do. Anyway, I got the decorator out of the class, now I am getting: "TypeError: anonimize..inner() takes 1 positional argument but 2 were given" – swapman Dec 30 '21 at 13:50
  • @swapman [this thread](https://stackoverflow.com/questions/1367514/how-to-decorate-a-method-inside-a-class) should help you with the TypeError – Expurple Dec 30 '21 at 14:29
0

Ok, so this was the wrong version I posted:

def anonimize(func):
    def inner(text):
        print(re.sub("[A-Z][a-z]+\s[A-Z][a-z]+", "***", re.sub("\S+@\S+\.\S+", "***", re.sub("\d{5,}", "***", text))))
        return func(text)
    return inner

And this is the way it should have been:

def anonimize(func):
    def inner(self, text):
        text = re.sub("[A-Z][a-z]+\s[A-Z][a-z]+", "***", re.sub("\S+@\S+\.\S+", "***", re.sub("\d{5,}", "***", text)))
        return func(self, text)
    return inner
swapman
  • 11
  • 2
  • I asked what caused my program crash. The answer was that I didn't use 'self' when defining a method inside a class. – swapman Dec 30 '21 at 20:02