IT

파이썬에서 클래스를 어떻게 디자인합니까?

lottoking 2020. 6. 19. 07:57
반응형

파이썬에서 클래스를 어떻게 디자인합니까?


발에서 발발가락 을 감지하는 이전 질문에 정말 도움 이되었지만 이러한 모든 솔루션은 한 번에 한 번만 측정됩니다.

이제 나는 구성된 데이터 가 있습니다.

  • 약 30 마리;
  • 각각 24 개의 측정 값이 있습니다 (여러 개의 하위 그룹으로 나눔).
  • 각 측정에는 4 개 이상의 접점 (각 발마다 하나씩)이 있으며
    • 각 접점은 5 개 부분으로 나누어 져 있으며
    • 접촉 시간, 위치, 총 힘 등과 같은 여러 매개 변수가 있습니다.

대체 텍스트

분명히 모든 것을 하나의 큰 객체에 집어 넣는 것은 자르지 않을 것이므로 현재의 많은 함수 대신 클래스를 사용해야한다고 생각했습니다. 그러나 클래스에 대한 Python 학습 장을 읽었지만 내 코드에 적용하지 못합니다 ( GitHub 링크 )

또한 정보를 얻을 때마다 모든 데이터를 처리하는 것이 다소 이상하다고 생각 합니다. 각 발의 위치를 ​​알고 나면이를 다시 계산할 이유가 없습니다. 또한, 같은 강아지의 모든 발을 비교하여 어떤 접촉이 어느 발에 속하는지 결정합니다 (앞 / 뒤, 왼쪽 / 오른쪽). 함수 만 계속 사용하면 문제가 될 수 있습니다.

그래서 나는 합리적인 방식으로 내 데이터 ( 한 개의 압축 된 데이터에 연결)를 처리 할 수있는 클래스를 만드는 방법에 대한 조언을 찾고 있습니다 .


수업을 디자인하는 방법.

  1. 단어를 적어 두십시오. 이 작업을 시작했습니다. 어떤 사람들은 왜 문제가 있는지 궁금해하지 않습니다.

  2. 단어 집합을 이러한 개체가 수행 할 작업에 대한 간단한 설명으로 확장하십시오. 다시 말해,이 일들에 대해 수행 할 다양한 계산을 적어 두십시오. 연락처 당 30 개의 개, 24 개의 측정, 4 개의 연락처 및 여러 개의 "매개 변수"목록이 흥미롭지 만 이야기의 일부일뿐입니다. 개체 디자인의 다음 단계는 "각 발의 위치"와 "같은 발의 모든 발을 비교하여 어떤 접촉이 어느 발에 속하는지 결정합니다"입니다.

  3. 명사에 밑줄을 긋습니다. 진심으로. 일부 사람들은 이것의 가치에 대해 토론하지만 처음 OO 개발자에게는 도움이된다는 것을 알았습니다. 명사에 밑줄을 긋습니다.

  4. 명사를 검토하십시오. "매개 변수"및 "측정"과 같은 일반 명사는 문제 영역의 문제에 적용되는 구체적이고 구체적인 명사로 대체해야합니다. 구체적인 사항은 문제를 명확히하는 데 도움이됩니다. 제네릭은 단순히 세부 사항을 제거합니다.

  5. 각 명사 ( "contact", "paw", "dog"등)에 대해 해당 명사의 속성과 해당 개체가 관여하는 동작을 기록하십시오. 이것을 짧게 자르지 마십시오. 모든 속성. 예를 들어 "데이터 세트에 30 마리의 개가 포함되어 있습니다"는 중요합니다.

  6. 각 속성에 대해, 이것이 정의 된 명사 또는 문자열이나 부동 소수점 같은 다른 종류의 "원시"또는 "원자"데이터와의 관계인지 여부를 식별 할 수 없는지 식별하십시오.

  7. 각 조치 또는 조작마다 책임이있는 명사와 참여하는 명사를 식별해야합니다. "변동성"의 문제입니다. 일부 개체는 업데이트되고 다른 개체는 업데이트되지 않습니다. 가변성 개체는 돌연변이에 대한 전적인 책임을 가져야합니다.

  8. 이 시점에서 명사를 클래스 정의로 변환 할 수 있습니다. 일부 집단 명사는 목록, 사전, 튜플, 세트 또는 명명 된 튜플이므로 많은 작업을 수행 할 필요가 없습니다. 다른 클래스는 복잡한 파생 데이터 또는 일부 업데이트 / 돌연변이로 인해 더 복잡합니다.

unittest를 사용하여 각 클래스를 개별적으로 테스트하는 것을 잊지 마십시오.

또한 클래스를 변경할 수 있어야한다고 말하는 법은 없습니다. 예를 들어, 변경 가능한 데이터가 거의 없습니다. 당신이 가진 것은 소스 데이터 셋의 변환 함수에 의해 생성 된 파생 된 데이터입니다.


다음 충고 (@ S.Lott의 충고와 유사)는 Beginning Python : 초보자부터 전문가까지

  1. 문제에 대한 설명을 작성하십시오 (문제는 어떻게해야합니까?). 모든 명사, 동사 및 형용사에 밑줄을 긋습니다.

  2. 잠재적 클래스를 찾기 위해 명사를 살펴보십시오.

  3. 동사를 살펴보고 잠재적 인 방법을 찾으십시오.

  4. 형용사를 살펴보고 잠재적 인 속성을 찾으십시오.

  5. 메소드와 속성을 클래스에 할당

수업을 개선하기 위해이 책은 또한 우리가 다음을 할 수 있다고 조언합니다.

  1. 프로그램 사용 방법에 대한 시나리오 인 일련의 사용 사례를 작성 하십시오. 모든 기능을 다룰 것.

  2. 모든 사용 사례를 단계별로 생각하여 필요한 모든 것을 다룰 수 있도록하십시오.


저는 TDD 접근 방식이 마음에 듭니다. 따라서 원하는 행동에 대한 테스트를 작성하여 시작하십시오. 그리고 통과하는 코드를 작성하십시오. 이 시점에서 디자인에 대해 너무 걱정하지 말고 테스트 스위트와 소프트웨어를 구입하십시오. 복잡한 방법으로 하나의 큰 추악한 클래스로 끝나더라도 걱정하지 마십시오.

때때로,이 초기 프로세스 중에 테스트 가능성을 위해 테스트하기 어렵고 분해해야하는 동작을 찾을 수 있습니다. 이것은 별도의 클래스가 필요하다는 힌트 일 수 있습니다.

그렇다면 재미있는 부분은 ... 리팩토링입니다. 소프트웨어를 작업 한 후에 복잡한 부분을 볼 수 있습니다. 종종 행동의 작은 포켓이 분명 해져서 새로운 클래스를 제안하지만 그렇지 않은 경우 코드를 단순화하는 방법을 찾으십시오. 서비스 객체 및 값 객체를 추출합니다. 방법을 단순화하십시오.

git을 올바르게 사용하고 있다면 (git을 사용하고 있습니까?) 리팩토링 중에 특정 분해를 매우 빠르게 실험 한 다음 포기하고 다시 단순화하지 않으면 되돌릴 수 있습니다.

테스트 된 작업 코드를 먼저 작성하면 디자인 우선 접근 방식으로는 쉽게 얻을 수없는 문제 영역에 대한 통찰력을 얻을 수 있습니다. 테스트와 코드 작성은 "어디에서 시작해야합니까"마비를지나칩니다.


The whole idea of OO design is to make your code map to your problem, so when, for example, you want the first footstep of a dog, you do something like:

dog.footstep(0)

Now, it may be that for your case you need to read in your raw data file and compute the footstep locations. All this could be hidden in the footstep() function so that it only happens once. Something like:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[This is now a sort of caching pattern. The first time it goes and reads the footstep data, subsequent times it just gets it from self._footsteps.]

But yes, getting OO design right can be tricky. Think more about the things you want to do to your data, and that will inform what methods you'll need to apply to what classes.


Writing out your nouns, verbs, adjectives is a great approach, but I prefer to think of class design as asking the question what data should be hidden?

Imagine you had a Query object and a Database object:

The Query object will help you create and store a query -- store, is the key here, as a function could help you create one just as easily. Maybe you could stay: Query().select('Country').from_table('User').where('Country == "Brazil"'). It doesn't matter exactly the syntax -- that is your job! -- the key is the object is helping you hide something, in this case the data necessary to store and output a query. The power of the object comes from the syntax of using it (in this case some clever chaining) and not needing to know what it stores to make it work. If done right the Query object could output queries for more then one database. It internally would store a specific format but could easily convert to other formats when outputting (Postgres, MySQL, MongoDB).

Now let's think through the Database object. What does this hide and store? Well clearly it can't store the full contents of the database, since that is why we have a database! So what is the point? The goal is to hide how the database works from people who use the Database object. Good classes will simplify reasoning when manipulating internal state. For this Database object you could hide how the networking calls work, or batch queries or updates, or provide a caching layer.

The problem is this Database object is HUGE. It represents how to access a database, so under the covers it could do anything and everything. Clearly networking, caching, and batching are quite hard to deal with depending on your system, so hiding them away would be very helpful. But, as many people will note, a database is insanely complex, and the further from the raw DB calls you get, the harder it is to tune for performance and understand how things work.

This is the fundamental tradeoff of OOP. If you pick the right abstraction it makes coding simpler (String, Array, Dictionary), if you pick an abstraction that is too big (Database, EmailManager, NetworkingManager), it may become too complex to really understand how it works, or what to expect. The goal is to hide complexity, but some complexity is necessary. A good rule of thumb is to start out avoiding Manager objects, and instead create classes that are like structs -- all they do is hold data, with some helper methods to create/manipulate the data to make your life easier. For example, in the case of EmailManager start with a function called sendEmail that takes an Email object. This is a simple starting point and the code is very easy to understand.

As for your example, think about what data needs to be together to calculate what you are looking for. If you wanted to know how far an animal was walking, for example, you could have AnimalStep and AnimalTrip (collection of AnimalSteps) classes. Now that each Trip has all the Step data, then it should be able to figure stuff out about it, perhaps AnimalTrip.calculateDistance() makes sense.


After skimming your linked code, it seems to me that you are better off not designing a Dog class at this point. Rather, you should use Pandas and dataframes. A dataframe is a table with columns. You dataframe would have columns such as: dog_id, contact_part, contact_time, contact_location, etc. Pandas uses Numpy arrays behind the scenes, and it has many convenience methods for you:

  • 예를 들어 개를 선택하십시오 : my_measurements['dog_id']=='Charly'
  • 데이터를 저장하십시오. my_measurements.save('filename.pickle')
  • pandas.read_csv()텍스트 파일을 수동으로 읽는 대신 사용하는 것이 좋습니다.

참고 URL : https://stackoverflow.com/questions/4203163/how-do-i-design-a-class-in-python

반응형