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
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:
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.
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.
| Situation | Will it work? | Why |
|---|---|---|
self.__read_file() in __init__ | โ Yes | Automatically reads the file on object creation |
Skip self.__read_file() | โ Not directly | You'll have to manually call it using the mangled name |
Call p.__read_file() | โ No | Python hides it (name mangling) |
Call p._FileProcessor__read_file() | โ Yes | You 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:
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)
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!โ
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.
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().
min() โ built-in functionmin(self) โ recursive trap!builtins.min() if you want to keep the name.