## ๐Ÿ“ Python FileProcessor Class โ€” Reading and Analyzing Numbers from a File

Yeeunยท2025๋…„ 4์›” 23์ผ

Python

๋ชฉ๋ก ๋ณด๊ธฐ
12/31

๐Ÿ“ Python FileProcessor Class โ€” Reading and Analyzing Numbers from a File

When working with data files, itโ€™s common to extract numbers and calculate basic statistics like sum, average, min, and max. Today, Iโ€™ll walk you through a simple but powerful Python class called FileProcessor that does exactly that! ๐Ÿงฎ


๐Ÿ’ก ๋ชฉํ‘œ

์ด ๊ธ€์—์„œ๋Š” ๋‹ค์Œ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค:

  • ํŒŒ์ผ์—์„œ ์ˆซ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๊ธฐ
  • ์ฝ์–ด์˜จ ์ˆซ์ž ๋ฆฌ์ŠคํŠธ์˜ ํ•ฉ๊ณ„, ํ‰๊ท , ์ตœ์†Œ, ์ตœ๋Œ€๊ฐ’ ๊ณ„์‚ฐํ•˜๊ธฐ
  • ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ๋กœ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๊ธฐ

๐Ÿ“ฆ ํŒŒ์ผ ์˜ˆ์‹œ (Numbers.txt)

1.5
2.3
5.0
4.1
-1.2

โœ… FileProcessor ํด๋ž˜์Šค ์ฝ”๋“œ

class FileProcessor:
    def __init__(self, file_name):
        self.file_name = file_name
        self.data = []
        self.__read_file()  # ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ํŒŒ์ผ ์ฝ๊ธฐ ์‹คํ–‰

    def __read_file(self):
        with open(self.file_name, 'r') as f:
            for line in f:
                try:
                    number = float(line.rstrip())  # ์ค„ ๋ ๊ฐœํ–‰ ์ œ๊ฑฐ ํ›„ ์‹ค์ˆ˜ํ˜• ๋ณ€ํ™˜
                    self.data.append(number)
                except ValueError:
                    continue  # ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†๋Š” ์ค„์€ ๋ฌด์‹œ

    def sum(self):
        return sum(self.data)

    def average(self):
        if len(self.data) == 0:
            raise ZeroDivisionError("No data to average")
        return sum(self.data) / len(self.data)

    def min(self):
        return min(self.data)

    def max(self):
        return max(self.data)

๐Ÿ” ์‚ฌ์šฉ ์˜ˆ์‹œ

p = FileProcessor('Numbers.txt')

print("Data:", p.data)
print("ํ•ฉ๊ณ„:", p.sum())
print("ํ‰๊ท :", p.average())
print("์ตœ์†Œ๊ฐ’:", p.min())
print("์ตœ๋Œ€๊ฐ’:", p.max())

๐Ÿ› ๏ธ ๋‚ด๋ถ€ ์ž‘๋™ ์„ค๋ช…

  • __init__(): ํŒŒ์ผ ์ด๋ฆ„ ์ €์žฅ ๋ฐ ์ž๋™์œผ๋กœ __read_file() ์‹คํ–‰
  • __read_file(): ์ค„ ๋‹จ์œ„๋กœ ์ฝ๊ณ  float์œผ๋กœ ๋ณ€ํ™˜ ํ›„ self.data์— ์ €์žฅ
  • sum(), average(), min(), max(): ๊ฐ๊ฐ์˜ ํ†ต๊ณ„ ์ •๋ณด๋ฅผ ๋ฆฌํ„ด

๐Ÿ™‹โ€โ™€๏ธ ๊ฟ€ํŒ

  • ์ˆจ๊ฒจ์ง„ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ: __read_file()์€ __๋กœ ์‹œ์ž‘ํ•ด ์™ธ๋ถ€์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์–ด์š”. (name mangling)
    • ์›ํ•˜๋ฉด p._FileProcessor__read_file() ์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์˜ˆ์™ธ ์ฒ˜๋ฆฌ: ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ๋กœ ์ธํ•ด ํ”„๋กœ๊ทธ๋žจ์ด ๋ฉˆ์ถ”์ง€ ์•Š๋„๋ก try-except๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”š ๋งˆ๋ฌด๋ฆฌ

์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ FileProcessor ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ์ˆซ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ๊ฐ„๋‹จํ•œ ํ†ต๊ณ„ ์ •๋ณด๋ฅผ ์ž๋™์œผ๋กœ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฐ์ดํ„ฐ ๋ถ„์„์ด๋‚˜ ๋กœ๊ทธ ํŒŒ์ผ ์ „์ฒ˜๋ฆฌ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•ด๋ณด์„ธ์š”!


Yes, exactly! ๐Ÿ˜„

When you use dir(object) in Python, it returns all the attributes and methods of that object โ€” including the "hidden" or private ones that start with double underscores (__), which are name-mangled by Python.

๐Ÿ” ์˜ˆ์‹œ๋กœ ๋‹ค์‹œ ์‚ดํŽด๋ณผ๊ฒŒ์š”:

p = FileProcessor('Numbers.txt')
print(dir(p))

์ถœ๋ ฅ ๊ฒฐ๊ณผ์—๋Š” ์ด๋Ÿฐ ๊ฒŒ ํฌํ•จ๋˜์–ด ์žˆ์„ ๊ฑฐ์˜ˆ์š”:

['_FileProcessor__read_file', ...]

Python์€ __read_file() ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์™ธ๋ถ€์—์„œ ์‹ค์ˆ˜๋กœ ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํด๋ž˜์Šค์ด๋ฆ„__๋ฉ”์„œ๋“œ์ด๋ฆ„ ํ˜•์‹์œผ๋กœ ๋ฐ”๊ฟ” ์ €์žฅํ•ด์š”. ์ด๋ฅผ name mangling์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”“ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•

p._FileProcessor__read_file()

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์€๋‹‰๋œ ๋ฉ”์„œ๋“œ๋„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์–ด์š”, ๋‹ค๋งŒ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ์ž๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ง๋ผ๋Š” ๋œป์œผ๋กœ __๋กœ ๊ฐ์ถฐ๋†“์€ ๊ฑฐ๋‹ˆ, ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์จ์•ผ ํ•ด์š”.


๐Ÿง  ์š”์•ฝ

  • dir()๋กœ ์ˆจ๊ฒจ์ง„ ๋ฉ”์„œ๋“œ ํ™•์ธ ๊ฐ€๋Šฅ
  • __method() โ†’ ._ClassName__method() ํ˜•ํƒœ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ
  • ๋ณด์•ˆ ๋ชฉ์ ์ด ์•„๋‹ˆ๋ผ ์‹ค์ˆ˜ ๋ฐฉ์ง€ ๋ชฉ์ ์ด๋‹ˆ ๋„ˆ๋ฌด ๋ฏฟ์ง€ ๋ง ๊ฒƒ!

Great question โ€” and you're thinking deeply, which is awesome! ๐Ÿ’ก Let's break it down gently and clearly:


โœ… What happens if you do not call self.__read_file() in __init__?

Your FileProcessor class is written like this:

class FileProcessor:
    def __init__(self, file_name):
        self.file_name = file_name
        self.data = []
        # self.__read_file()  โ† This is what you're asking about

If you don't call self.__read_file() here, then self.data will stay an empty list, and any of the following calls:

p = FileProcessor("Numbers.txt")
p.average()

will raise this error:

ZeroDivisionError

Because len(self.data) is still 0.


โ— But what if I try to call the hidden method directly?

You might think:

p.__read_file()

But this gives:

AttributeError: 'FileProcessor' object has no attribute '__read_file'

Because Python renamed it (name mangling).

If you want to force the method call, you can do this:

p._FileProcessor__read_file()

Then your .data list will be filled properly, and your .average() method will work.


โœ… Summary

SituationWill it work?Why
self.__read_file() in __init__โœ… YesAutomatically reads the file on object creation
Skip self.__read_file()โŒ Not directlyYou'll have to manually call it using the mangled name
Call p.__read_file()โŒ NoPython hides it (name mangling)
Call p._FileProcessor__read_file()โœ… YesYou bypass the name mangling and call it manually

That line:

if len(self.data) == 0: raise ZeroDivisionError

is a way to manually raise an error when there's no data in the list. Let me explain gently and clearly:


๐ŸŒฑ What does it mean?

It checks if the self.data list is empty. If it is, it stops the program and raises a ZeroDivisionError.

In context, it looks like this:

def average(self):
    if len(self.data) == 0:
        raise ZeroDivisionError
    return sum(self.data) / len(self.data)

๐Ÿค” Why raise a ZeroDivisionError?

Because dividing by zero (which would happen if the list is empty) is a mathematical error. This line is basically saying:

โ€œThere's nothing to average โ€” we can't divide by zero!โ€


๐Ÿ’ก Friendlier way (optional improvement)

Raising an error is correct, but you could also show a message:

if len(self.data) == 0:
    raise ZeroDivisionError("Cannot calculate average from empty data.")

Now when the error happens, it tells you why.


This line:

def min(self):
    return min(self.data)

causes a recursion error because you're defining a method named min, and then inside that method, you're trying to call min(self.data) โ€” but Python now thinks you're trying to call your own method again, not the built-in min() function. This leads to infinite recursion.


๐Ÿง  How to fix it?

You can fix it by telling Python to use the built-in min() function explicitly using the builtins module:

import builtins

def min(self):
    return builtins.min(self.data)

Or, better yet, to avoid confusion, you can rename your method:

def get_min(self):
    return min(self.data)

This keeps your code clear and avoids name conflicts. The same goes for max().


Summary:

  • โœ… min() โ€” built-in function
  • ๐Ÿšซ min(self) โ€” recursive trap!
  • ๐Ÿ’ก Rename your method or use builtins.min() if you want to keep the name.

0๊ฐœ์˜ ๋Œ“๊ธ€