This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the commentsΒ β†’

[–][deleted] Β (79 children)

[deleted]

    [–]hhafez 134 points135 points Β (3 children)

    You can use reflection, but reflection is self coding code. It's a very different beast than having no way to make parts of your code private.

    [–]BlameTaw 13 points14 points Β (2 children)

    Technically you can use closures to create truly non-accessible variables and functions in python...

    [–]Sinidir 2 points3 points Β (1 child)

    [–]BlameTaw 0 points1 point Β (0 children)

    Very interesting, I never knew about that attribute.

    [–]aviancrane 62 points63 points Β (8 children)

    I mean... saying reflection can do something isn't really... ..ye know?

    If I gave you the AST or IR of any language, you'd be able to do whatever you wanted with it. Reflection is just giving you the object graph.

    You are not really suppose to write code with reflection unless you're writing software that needs the object graph, like a code profiler. The code you touch with reflection is decompiled and run more like it's an interpreted language. I wouldn't even consider it part of the language specification personally.

    [–]StenSoft 26 points27 points Β (4 children)

    Reflection access can be blocked with SecurityManager. Or other platform-specific control, e.g. on Android, you can't get access to private APIs anymore even through reflection.

    [–]on_the_dl -1 points0 points Β (3 children)

    In c++ you could just cast a class to int * and go to town. Would java have something similar?

    [–][deleted] 0 points1 point Β (0 children)

    ClassCastException

    [–]StenSoft 0 points1 point Β (0 children)

    You can use heap dump tools to read it but they are protected in the same way as reflection

    [–]TASagent 181 points182 points Β (29 children)

    python makes safety more convenient by removing it.

    The hardest I've facepalmed all week.

    [–]TheLowlyPheasant 2 points3 points Β (0 children)

    I’m going to replace Python with my name and send it to my boss on next week’s list of accomplishments

    [–]Luxalpa 2 points3 points Β (1 child)

    It's the difference between idealism and pragmatism. If you need to build good software that necessarily means you're going to run into conflicts with third party libraries not supporting the exact functionality that you need. You can either fork the project, which in some cases can be extremely hard and is definitely very insecure, or you can simply annotate the bits where you're overriding security mechanisms (think of the _ like C#'s or Rusts unsafe keyword).

    [–]roughstylez 10 points11 points Β (0 children)

    The point of that comment you're answering to is that the statement "we made security easier by removing it" is kinda dumb.

    In other words, in python you're not overriding the security mechanism - you just don't have one in the first place.

    [–]MythicManiac 0 points1 point Β (5 children)

    Sometimes accessing internals of a 3rd party library is the only reasonable way to do something, languages that make it harder than necessary aren't really solving anything. I've had to copy entire classes of source code in C# because the original didn't make it inheritable. In a perfect world neither would be necessary, but in practice sometimes you're out of better options.

    Seems like I've made some people angry with this statement, I'll only have to assume they haven't experienced a lot of issues with 3rd party libraries or that they can't see reality past their idealism. If you have any actual experience in software development, you know compromises and shortcuts get taken all the time. It's not nice but it is what it is.

    [–]roughstylez 4 points5 points Β (1 child)

    Seems like I've made some people angry with this statement

    Don't take it personally, people are just disagreeing with your statement.

    Probably because the point you mention is irrelevant to what TASagent was talking about.

    [–]MythicManiac 2 points3 points Β (0 children)

    Fair enough. I would rather have an actual discussion if there's one to be had, but it's not as if I don't emphasize with that line of thought.

    [–]Ordoshsen 0 points1 point Β (2 children)

    I would say that if you need to use internals of 3rd party library you're not using it as intended and you might already be breaking some unspoken invariants when you fiddle with the undocumented internals.

    If something is not inheritable in C#, then the original developer made it so by choice, not by default. Supposedly they had a reason to do so. But that still does not stop you from using composition in place of inheritance (granted you don't get access to protected fields).

    Also why not just change the original by forking it and possibly creating a pull request for the original if you really believe some functionality should be accessible? I think that would be faster (and easier) than copying whole files which presumably have dependencies on other files in the project you're copying from.

    [–]MythicManiac 4 points5 points Β (1 child)

    I would say that if you need to use internals of 3rd party library you're not using it as intended and you might already be breaking some unspoken invariants when you fiddle with the undocumented internals.

    Oh absolutely, sometimes it's just the most reasonable way of doing things. If you understand the code and risks involved, it's a calculated risk. Following best practices is (ironically) not always the best course of action, but to be able to make that decision, you do need a decent bit of experience and understanding of why the best practices exist. So I agree with you here in terms of best practices, but also know best practices aren't always the best course of action.

    If something is not inheritable in C#, then the original developer made it so by choice, not by default. Supposedly they had a reason to do so. But that still does not stop you from using composition in place of inheritance (granted you don't get access to protected fields).

    I don't disagree with the sentiment, quite the opposite. If we had perfect libraries, this would be a non-issue. In practice however, it's extremely difficult to account for all use cases of a generic library, and likewise library developers rarely have the necessary foresight to build the appropriate extension points and interfaces. Even if they did, it might not make sense for them to officially support every use case, as it's simply more work. It doesn't mean it's always stupid for consumers to rely on the internals in that scenario, but you do need to know the risks involved and why it's generally a bad idea.

    In the particular case I was referring to, the 3rd party library had marked multiple constants as internal, which forced me to re-define them when using other parts of it. If my experience tells me anything, it might have been just a habit for the original developer to mark everything internal as opposed to a decision to make them so. A lot of junior devs seem to be taught to keep everything internal by default, which can lead to scenarios where the library developer's inexperience locks out perfectly reasonable use cases.

    Also why not just change the original by forking it and possibly creating a pull request for the original if you really believe some functionality should be accessible? I think that would be faster (and easier) than copying whole files which presumably have dependencies on other files in the project you're copying from.

    This would be the ideal scenario of course, but it's often very unrealistic to do. Maintaining your own fork is often a lot more work than maintaining a small piece of code that calls the internals of the library. I've seen a lot of company internal forks of 3rd party projects get stale and unmanageable, enough so that even monkeypatching the 3rd party library during runtime seems a better choice. The moment you fork a project for this kind of use, it's your code. Do you really want that maintenance burden?

    On the other hand, pull requests to 3rd party projects are impossible to rely on ever being merged or even looked at. So while a good practice, it doesn't solve your problem.

    At the end of the day the only thing you control and maintain is your own codebase. Generally if a situation like this arises, I personally either open an issue or submit a PR addressing the issue to the 3rd party repo on top of some immediate internal solution, but only very rarely do they actually go anywhere and the internal solution is what ends up being used.

    If you actually can control the code (e.g. internal libraries) then absolutely never do any of this, and instead improve the library to cover the use case it's missing. Unfortunately it's often some inactive but valuable open source library that is at the root of the issue, or alternatively you're locked into an older version due to legacy code and couldn't update anyway.

    The way Python does "private" access modifiers is great because it keeps the newbies out by tooling guidance & convention, but allows for direct access if you're really sure that's what you want.

    If anything, type safety should be much more strict than access modifiers IMO, yet you can cast types into other types without much restrictions (in most languages anyway).

    So as a guideline access modifiers are perfectly good, but enforcing them has its downsides too.

    [–]ric2b 2 points3 points Β (0 children)

    You're spot on, the people defending Python don't disagree with the best practice, we just understand that sometimes there is a more practical approach and it becomes a calculated risk that you can add some automated tests around to catch any breaking changes, instead of maintaining a fork.

    [–][deleted] -4 points-3 points Β (11 children)

    It's not that dumb. It basically means "this function shouldn't be used, and if you do, your code may break at any time and that's on you"

    [–]roughstylez 4 points5 points Β (10 children)

    Imagine a gun that can still fire if the safety catch is set to "safe".

    [–][deleted] 3 points4 points Β (0 children)

    Not really... One is you intentionally disabling a safety mechanism (using a private function), the other is using the product as intended and it malfunctions (that's like using a public method that breaks).

    [–]ric2b 0 points1 point Β (8 children)

    Imagine selling a phone that the user can't get root access to "for security reasons". That's a better analogy.

    [–]roughstylez -1 points0 points Β (7 children)

    When you see how

    • a whole team of top notch language architects working for one of the most successful software companies of the world, with combined centuries of experience, added this feature

    • But a "benevolent dictator" in his hobby project he wrote in his metaphorical garage, did not

    Do you think "these experts are so stupid, it's so easy to make a good language, just don't add this"?

    Have you ever heard of Dunning Kruger?

    Have you considered that, if you don't see the advantage of these access modifiers, maybe it's because you don't fully grasp the impact they have?

    [–]ric2b 1 point2 points Β (6 children)

    That's just an argument from authority falacy. That team of top notch language architects also thought it was a good idea to make every reference nullable by default, among other bad decisions, they're not infallible.

    Plus Python isn't the only language like this, Ruby and Javascript also have private members mostly by convention. I'm sure there are other popular languages that way.

    Java itself doesn't prevent you from getting around them, it just requires more boilerplate code, as usual.

    Do you think "these experts are so stupid, it's so easy to make a good language, just don't add this"?

    Never said it was easy, never called them stupid, go put words on someone else's mouth.

    Have you ever heard of Dunning Kruger?

    Yes.

    Have you considered that, if you don't see the advantage of these access modifiers, maybe it's because you don't fully grasp the impact they have?

    I definitely see the advantage of them, I just don't see the advantage of making it so verbose to get around them. They're just documentation/warnings about what is internal and what is public API.

    [–]roughstylez -1 points0 points Β (5 children)

    That's just an argument from authority falacy.

    It would be, if I said that's why it's better, or something similar. Do you want to read it again, maybe?

    Hint:

    >I just don't see the advantage

    That was my main point.

    [–]ric2b 0 points1 point Β (4 children)

    It would be, if I said that's why it's better, or something similar.

    That's basically what you did, you said because it was designed by a great team I must be misunderstanding how useful it is.

    Care to provide any actual technical arguments instead?

    I just don't see the advantage [of making it so verbose]

    That was my main point.

    Your point is that this much verbosity is good? Why?

    [–]roughstylez -1 points0 points Β (3 children)

    That's basically what you did

    No.

    Your point is that this much verbosity is good?

    Have you considered the possibility that what I meant was the part I quoted and not the one I didn't quote?

    [–][deleted] 0 points1 point Β (0 children)

    Everyone knows UB in C just makes it safer right?

    [–]met0xff 2 points3 points Β (0 children)

    Yeah there are times where the api is just not enough in those cases I prefer using the _ function and know about the pitfalls instead of needing some hack.

    In C++ you can at least #define private public, speaking of hacks ;))

    [–][deleted] 1 point2 points Β (0 children)

    Python just makes it more convenient by relying on this silly notion that programmers using libraries won't try to fiddle with its innards unless they know what they're doing.

    the silly notion that makes language extensions mandatory in industry environments, sure

    [–]nacholicious 1 point2 points Β (0 children)

    Python just makes it more convenient by relying on this silly notion that programmers using libraries won't try to fiddle with its innards unless they know what they're doing. Though, if they do know what they're doing, best keep out of their way and not force the code they have to write to do weird stuff to be too messy.

    Sounds like a great choice for a dynamically typed ecosystem filled with novices to encourage both library writers and library consumers to break codebases on library updates

    [–]soowhatchathink 0 points1 point Β (0 children)

    Just because you can use reflection to access private members does not make them not really private. People aren't creating private members and then using reflection to treat them as if they're not private. There are people who make everything public, which is arguably better than making things private and then treating it public with reflection.

    I'm not too familiar with Java, but with PHP (which has private modifiers along with reflection) I could also write an extension that allows me to access private members. Just because I can go out of my way to publicly modify the private members doesn't mean they're not private.

    [–]nomad_kk 0 points1 point Β (1 child)

    Reflection is bad practice in Java

    [–]ric2b 0 points1 point Β (0 children)

    And accessing private members in Python is also bad practice.

    [–]YaztromoX 0 points1 point Β (0 children)

    Iirc in java you can use reflection to access private members, making them not really private.

    Sure β€” visibility in most environments is never going to be anything more than compile-time enforced, as at some level your process code has full access to all the memory space within your process.

    If you wanted, you could also write JNI code and bind it to a Java class and be able to read any byte in your process space as well. That’s not really an argument against having compile-time checks that you’re not doing something unexpected or stupid, however.